Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next

Pull networking updates from David Miller:
 "Highlights:

   1) Steady transitioning of the BPF instructure to a generic spot so
      all kernel subsystems can make use of it, from Alexei Starovoitov.

   2) SFC driver supports busy polling, from Alexandre Rames.

   3) Take advantage of hash table in UDP multicast delivery, from David
      Held.

   4) Lighten locking, in particular by getting rid of the LRU lists, in
      inet frag handling.  From Florian Westphal.

   5) Add support for various RFC6458 control messages in SCTP, from
      Geir Ola Vaagland.

   6) Allow to filter bridge forwarding database dumps by device, from
      Jamal Hadi Salim.

   7) virtio-net also now supports busy polling, from Jason Wang.

   8) Some low level optimization tweaks in pktgen from Jesper Dangaard
      Brouer.

   9) Add support for ipv6 address generation modes, so that userland
      can have some input into the process.  From Jiri Pirko.

  10) Consolidate common TCP connection request code in ipv4 and ipv6,
      from Octavian Purdila.

  11) New ARP packet logger in netfilter, from Pablo Neira Ayuso.

  12) Generic resizable RCU hash table, with intial users in netlink and
      nftables.  From Thomas Graf.

  13) Maintain a name assignment type so that userspace can see where a
      network device name came from (enumerated by kernel, assigned
      explicitly by userspace, etc.) From Tom Gundersen.

  14) Automatic flow label generation on transmit in ipv6, from Tom
      Herbert.

  15) New packet timestamping facilities from Willem de Bruijn, meant to
      assist in measuring latencies going into/out-of the packet
      scheduler, latency from TCP data transmission to ACK, etc"

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1536 commits)
  cxgb4 : Disable recursive mailbox commands when enabling vi
  net: reduce USB network driver config options.
  tg3: Modify tg3_tso_bug() to handle multiple TX rings
  amd-xgbe: Perform phy connect/disconnect at dev open/stop
  amd-xgbe: Use dma_set_mask_and_coherent to set DMA mask
  net: sun4i-emac: fix memory leak on bad packet
  sctp: fix possible seqlock seadlock in sctp_packet_transmit()
  Revert "net: phy: Set the driver when registering an MDIO bus device"
  cxgb4vf: Turn off SGE RX/TX Callback Timers and interrupts in PCI shutdown routine
  team: Simplify return path of team_newlink
  bridge: Update outdated comment on promiscuous mode
  net-timestamp: ACK timestamp for bytestreams
  net-timestamp: TCP timestamping
  net-timestamp: SCHED timestamp on entering packet scheduler
  net-timestamp: add key to disambiguate concurrent datagrams
  net-timestamp: move timestamp flags out of sk_flags
  net-timestamp: extend SCM_TIMESTAMPING ancillary data struct
  cxgb4i : Move stray CPL definitions to cxgb4 driver
  tcp: reduce spurious retransmits due to transient SACK reneging
  qlcnic: Initialize dcbnl_ops before register_netdev
  ...
This commit is contained in:
Linus Torvalds 2014-08-06 09:38:14 -07:00
commit ae045e2455
1159 changed files with 70927 additions and 30829 deletions

6
net/6lowpan/Kconfig Normal file
View file

@ -0,0 +1,6 @@
config 6LOWPAN
bool "6LoWPAN Support"
depends on IPV6
---help---
This enables IPv6 over Low power Wireless Personal Area Network -
"6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.

3
net/6lowpan/Makefile Normal file
View file

@ -0,0 +1,3 @@
obj-$(CONFIG_6LOWPAN) := 6lowpan.o
6lowpan-y := iphc.o

View file

@ -3,8 +3,7 @@
* written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*/
/*
* Based on patches from Jon Smirl <jonsmirl@gmail.com>
/* Based on patches from Jon Smirl <[email protected]>
* Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
@ -58,16 +57,15 @@
#include <net/ipv6.h>
#include <net/af_ieee802154.h>
/*
* Uncompress address function for source and
/* Uncompress address function for source and
* destination address(non-multicast).
*
* address_mode is sam value or dam value.
*/
static int uncompress_addr(struct sk_buff *skb,
struct in6_addr *ipaddr, const u8 address_mode,
const u8 *lladdr, const u8 addr_type,
const u8 addr_len)
struct in6_addr *ipaddr, const u8 address_mode,
const u8 *lladdr, const u8 addr_type,
const u8 addr_len)
{
bool fail;
@ -140,13 +138,12 @@ static int uncompress_addr(struct sk_buff *skb,
return 0;
}
/*
* Uncompress address function for source context
/* Uncompress address function for source context
* based address(non-multicast).
*/
static int uncompress_context_based_src_addr(struct sk_buff *skb,
struct in6_addr *ipaddr,
const u8 sam)
struct in6_addr *ipaddr,
const u8 sam)
{
switch (sam) {
case LOWPAN_IPHC_ADDR_00:
@ -175,13 +172,13 @@ static int uncompress_context_based_src_addr(struct sk_buff *skb,
}
static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
struct net_device *dev, skb_delivery_cb deliver_skb)
struct net_device *dev, skb_delivery_cb deliver_skb)
{
struct sk_buff *new;
int stat;
new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
GFP_ATOMIC);
GFP_ATOMIC);
kfree_skb(skb);
if (!new)
@ -196,7 +193,7 @@ static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
new->dev = dev;
raw_dump_table(__func__, "raw skb data dump before receiving",
new->data, new->len);
new->data, new->len);
stat = deliver_skb(new, dev);
@ -208,10 +205,9 @@ static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
/* Uncompress function for multicast destination address,
* when M bit is set.
*/
static int
lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
struct in6_addr *ipaddr,
const u8 dam)
static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
struct in6_addr *ipaddr,
const u8 dam)
{
bool fail;
@ -257,41 +253,41 @@ lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
}
raw_dump_inline(NULL, "Reconstructed ipv6 multicast addr is",
ipaddr->s6_addr, 16);
ipaddr->s6_addr, 16);
return 0;
}
static int
uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
static int uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
{
bool fail;
u8 tmp = 0, val = 0;
if (!uh)
goto err;
fail = lowpan_fetch_skb(skb, &tmp, 1);
fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
pr_debug("UDP header uncompression\n");
switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
case LOWPAN_NHC_UDP_CS_P_00:
fail |= lowpan_fetch_skb(skb, &uh->source, 2);
fail |= lowpan_fetch_skb(skb, &uh->dest, 2);
fail |= lowpan_fetch_skb(skb, &uh->source,
sizeof(uh->source));
fail |= lowpan_fetch_skb(skb, &uh->dest,
sizeof(uh->dest));
break;
case LOWPAN_NHC_UDP_CS_P_01:
fail |= lowpan_fetch_skb(skb, &uh->source, 2);
fail |= lowpan_fetch_skb(skb, &val, 1);
fail |= lowpan_fetch_skb(skb, &uh->source,
sizeof(uh->source));
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh->dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
break;
case LOWPAN_NHC_UDP_CS_P_10:
fail |= lowpan_fetch_skb(skb, &val, 1);
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh->source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
fail |= lowpan_fetch_skb(skb, &uh->dest, 2);
fail |= lowpan_fetch_skb(skb, &uh->dest,
sizeof(uh->dest));
break;
case LOWPAN_NHC_UDP_CS_P_11:
fail |= lowpan_fetch_skb(skb, &val, 1);
fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
uh->source = htons(LOWPAN_NHC_UDP_4BIT_PORT +
(val >> 4));
uh->dest = htons(LOWPAN_NHC_UDP_4BIT_PORT +
@ -300,7 +296,6 @@ uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
default:
pr_debug("ERROR: unknown UDP format\n");
goto err;
break;
}
pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
@ -311,11 +306,11 @@ uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
pr_debug_ratelimited("checksum elided currently not supported\n");
goto err;
} else {
fail |= lowpan_fetch_skb(skb, &uh->check, 2);
fail |= lowpan_fetch_skb(skb, &uh->check,
sizeof(uh->check));
}
/*
* UDP lenght needs to be infered from the lower layers
/* UDP length needs to be infered from the lower layers
* here, we obtain the hint from the remaining size of the
* frame
*/
@ -338,21 +333,21 @@ err:
static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb)
const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb)
{
struct ipv6hdr hdr = {};
u8 tmp, num_context = 0;
int err;
raw_dump_table(__func__, "raw skb data dump uncompressed",
skb->data, skb->len);
skb->data, skb->len);
/* another if the CID flag is set */
if (iphc1 & LOWPAN_IPHC_CID) {
pr_debug("CID flag is set, increase header with one\n");
if (lowpan_fetch_skb_u8(skb, &num_context))
if (lowpan_fetch_skb(skb, &num_context, sizeof(num_context)))
goto drop;
}
@ -360,12 +355,11 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
/* Traffic Class and Flow Label */
switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
/*
* Traffic Class and FLow Label carried in-line
/* Traffic Class and FLow Label carried in-line
* ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
*/
case 0: /* 00b */
if (lowpan_fetch_skb_u8(skb, &tmp))
if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
goto drop;
memcpy(&hdr.flow_lbl, &skb->data[0], 3);
@ -374,23 +368,21 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
(hdr.flow_lbl[0] & 0x0f);
break;
/*
* Traffic class carried in-line
/* Traffic class carried in-line
* ECN + DSCP (1 byte), Flow Label is elided
*/
case 2: /* 10b */
if (lowpan_fetch_skb_u8(skb, &tmp))
if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
goto drop;
hdr.priority = ((tmp >> 2) & 0x0f);
hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
break;
/*
* Flow Label carried in-line
/* Flow Label carried in-line
* ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
*/
case 1: /* 01b */
if (lowpan_fetch_skb_u8(skb, &tmp))
if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
goto drop;
hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
@ -407,7 +399,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
/* Next Header */
if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
/* Next header is carried inline */
if (lowpan_fetch_skb_u8(skb, &(hdr.nexthdr)))
if (lowpan_fetch_skb(skb, &hdr.nexthdr, sizeof(hdr.nexthdr)))
goto drop;
pr_debug("NH flag is set, next header carried inline: %02x\n",
@ -415,10 +407,11 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
}
/* Hop Limit */
if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I)
if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I) {
hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
else {
if (lowpan_fetch_skb_u8(skb, &(hdr.hop_limit)))
} else {
if (lowpan_fetch_skb(skb, &hdr.hop_limit,
sizeof(hdr.hop_limit)))
goto drop;
}
@ -428,13 +421,12 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
if (iphc1 & LOWPAN_IPHC_SAC) {
/* Source address context based uncompression */
pr_debug("SAC bit is set. Handle context based source address.\n");
err = uncompress_context_based_src_addr(
skb, &hdr.saddr, tmp);
err = uncompress_context_based_src_addr(skb, &hdr.saddr, tmp);
} else {
/* Source address uncompression */
pr_debug("source address stateless compression\n");
err = uncompress_addr(skb, &hdr.saddr, tmp, saddr,
saddr_type, saddr_len);
saddr_type, saddr_len);
}
/* Check on error of previous branch */
@ -450,16 +442,17 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
pr_debug("dest: context-based mcast compression\n");
/* TODO: implement this */
} else {
err = lowpan_uncompress_multicast_daddr(
skb, &hdr.daddr, tmp);
err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
tmp);
if (err)
goto drop;
}
} else {
err = uncompress_addr(skb, &hdr.daddr, tmp, daddr,
daddr_type, daddr_len);
daddr_type, daddr_len);
pr_debug("dest: stateless compression mode %d dest %pI6c\n",
tmp, &hdr.daddr);
tmp, &hdr.daddr);
if (err)
goto drop;
}
@ -468,11 +461,11 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
if (iphc0 & LOWPAN_IPHC_NH_C) {
struct udphdr uh;
struct sk_buff *new;
if (uncompress_udp_header(skb, &uh))
goto drop;
/*
* replace the compressed UDP head by the uncompressed UDP
/* replace the compressed UDP head by the uncompressed UDP
* header
*/
new = skb_copy_expand(skb, sizeof(struct udphdr),
@ -489,7 +482,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
raw_dump_table(__func__, "raw UDP header dump",
(u8 *)&uh, sizeof(uh));
(u8 *)&uh, sizeof(uh));
hdr.nexthdr = UIP_PROTO_UDP;
}
@ -504,8 +497,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
hdr.hop_limit, &hdr.daddr);
raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
sizeof(hdr));
raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr));
return skb_deliver(skb, &hdr, dev, deliver_skb);
@ -515,9 +507,9 @@ drop:
}
EXPORT_SYMBOL_GPL(lowpan_process_data);
static u8 lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift,
const struct in6_addr *ipaddr,
const unsigned char *lladdr)
static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
const struct in6_addr *ipaddr,
const unsigned char *lladdr)
{
u8 val = 0;
@ -526,24 +518,22 @@ static u8 lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift,
pr_debug("address compression 0 bits\n");
} else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
/* compress IID to 16 bits xxxx::XXXX */
memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2);
*hc06_ptr += 2;
lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[7], 2);
val = 2; /* 16-bits */
raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
*hc06_ptr - 2, 2);
*hc_ptr - 2, 2);
} else {
/* do not compress IID => xxxx::IID */
memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8);
*hc06_ptr += 8;
lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[4], 8);
val = 1; /* 64-bits */
raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
*hc06_ptr - 8, 8);
*hc_ptr - 8, 8);
}
return rol8(val, shift);
}
static void compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
static void compress_udp_header(u8 **hc_ptr, struct sk_buff *skb)
{
struct udphdr *uh = udp_hdr(skb);
u8 tmp;
@ -555,75 +545,75 @@ static void compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
pr_debug("UDP header: both ports compression to 4 bits\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_11;
lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source and destination port */
tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
} else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
LOWPAN_NHC_UDP_8BIT_PORT) {
pr_debug("UDP header: remove 8 bits of dest\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_01;
lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
lowpan_push_hc_data(hc06_ptr, &uh->source, sizeof(uh->source));
lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
/* destination port */
tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
} else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
LOWPAN_NHC_UDP_8BIT_PORT) {
pr_debug("UDP header: remove 8 bits of source\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_10;
lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* destination port */
lowpan_push_hc_data(hc06_ptr, &uh->dest, sizeof(uh->dest));
lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
} else {
pr_debug("UDP header: can't compress\n");
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_00;
lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
/* source port */
lowpan_push_hc_data(hc06_ptr, &uh->source, sizeof(uh->source));
lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
/* destination port */
lowpan_push_hc_data(hc06_ptr, &uh->dest, sizeof(uh->dest));
lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
}
/* checksum is always inline */
lowpan_push_hc_data(hc06_ptr, &uh->check, sizeof(uh->check));
lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
/* skip the UDP header */
skb_pull(skb, sizeof(struct udphdr));
}
int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *_daddr,
const void *_saddr, unsigned int len)
unsigned short type, const void *_daddr,
const void *_saddr, unsigned int len)
{
u8 tmp, iphc0, iphc1, *hc06_ptr;
u8 tmp, iphc0, iphc1, *hc_ptr;
struct ipv6hdr *hdr;
u8 head[100] = {};
int addr_type;
if (type != ETH_P_IPV6)
return -EINVAL;
hdr = ipv6_hdr(skb);
hc06_ptr = head + 2;
hc_ptr = head + 2;
pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength = %d\n"
"\tnexthdr = 0x%02x\n\thop_lim = %d\n\tdest = %pI6c\n",
hdr->version, ntohs(hdr->payload_len), hdr->nexthdr,
hdr->hop_limit, &hdr->daddr);
hdr->version, ntohs(hdr->payload_len), hdr->nexthdr,
hdr->hop_limit, &hdr->daddr);
raw_dump_table(__func__, "raw skb network header dump",
skb_network_header(skb), sizeof(struct ipv6hdr));
skb_network_header(skb), sizeof(struct ipv6hdr));
/*
* As we copy some bit-length fields, in the IPHC encoding bytes,
/* As we copy some bit-length fields, in the IPHC encoding bytes,
* we sometimes use |=
* If the field is 0, and the current bit value in memory is 1,
* this does not work. We therefore reset the IPHC encoding here
@ -638,49 +628,47 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
raw_dump_inline(__func__, "daddr",
(unsigned char *)_daddr, IEEE802154_ADDR_LEN);
raw_dump_table(__func__,
"sending raw skb network uncompressed packet",
skb->data, skb->len);
raw_dump_table(__func__, "sending raw skb network uncompressed packet",
skb->data, skb->len);
/*
* Traffic class, flow label
/* Traffic class, flow label
* If flow label is 0, compress it. If traffic class is 0, compress it
* We have to process both in the same time as the offset of traffic
* class depends on the presence of version and flow label
*/
/* hc06 format of TC is ECN | DSCP , original one is DSCP | ECN */
/* hc format of TC is ECN | DSCP , original one is DSCP | ECN */
tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
(hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
(hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
/* flow label can be compressed */
iphc0 |= LOWPAN_IPHC_FL_C;
if ((hdr->priority == 0) &&
((hdr->flow_lbl[0] & 0xF0) == 0)) {
((hdr->flow_lbl[0] & 0xF0) == 0)) {
/* compress (elide) all */
iphc0 |= LOWPAN_IPHC_TC_C;
} else {
/* compress only the flow label */
*hc06_ptr = tmp;
hc06_ptr += 1;
*hc_ptr = tmp;
hc_ptr += 1;
}
} else {
/* Flow label cannot be compressed */
if ((hdr->priority == 0) &&
((hdr->flow_lbl[0] & 0xF0) == 0)) {
((hdr->flow_lbl[0] & 0xF0) == 0)) {
/* compress only traffic class */
iphc0 |= LOWPAN_IPHC_TC_C;
*hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2);
hc06_ptr += 3;
*hc_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
memcpy(hc_ptr + 1, &hdr->flow_lbl[1], 2);
hc_ptr += 3;
} else {
/* compress nothing */
memcpy(hc06_ptr, hdr, 4);
memcpy(hc_ptr, hdr, 4);
/* replace the top byte with new ECN | DSCP format */
*hc06_ptr = tmp;
hc06_ptr += 4;
*hc_ptr = tmp;
hc_ptr += 4;
}
}
@ -690,13 +678,11 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
if (hdr->nexthdr == UIP_PROTO_UDP)
iphc0 |= LOWPAN_IPHC_NH_C;
if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
*hc06_ptr = hdr->nexthdr;
hc06_ptr += 1;
}
if ((iphc0 & LOWPAN_IPHC_NH_C) == 0)
lowpan_push_hc_data(&hc_ptr, &hdr->nexthdr,
sizeof(hdr->nexthdr));
/*
* Hop limit
/* Hop limit
* if 1: compress, encoding is 01
* if 64: compress, encoding is 10
* if 255: compress, encoding is 11
@ -713,87 +699,89 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
iphc0 |= LOWPAN_IPHC_TTL_255;
break;
default:
*hc06_ptr = hdr->hop_limit;
hc06_ptr += 1;
break;
lowpan_push_hc_data(&hc_ptr, &hdr->hop_limit,
sizeof(hdr->hop_limit));
}
addr_type = ipv6_addr_type(&hdr->saddr);
/* source address compression */
if (is_addr_unspecified(&hdr->saddr)) {
if (addr_type == IPV6_ADDR_ANY) {
pr_debug("source address is unspecified, setting SAC\n");
iphc1 |= LOWPAN_IPHC_SAC;
/* TODO: context lookup */
} else if (is_addr_link_local(&hdr->saddr)) {
iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
LOWPAN_IPHC_SAM_BIT, &hdr->saddr, _saddr);
pr_debug("source address unicast link-local %pI6c "
"iphc1 0x%02x\n", &hdr->saddr, iphc1);
} else {
pr_debug("send the full source address\n");
memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16);
hc06_ptr += 16;
if (addr_type & IPV6_ADDR_LINKLOCAL) {
iphc1 |= lowpan_compress_addr_64(&hc_ptr,
LOWPAN_IPHC_SAM_BIT,
&hdr->saddr, _saddr);
pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
&hdr->saddr, iphc1);
} else {
pr_debug("send the full source address\n");
lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr, 16);
}
}
addr_type = ipv6_addr_type(&hdr->daddr);
/* destination address compression */
if (is_addr_mcast(&hdr->daddr)) {
if (addr_type & IPV6_ADDR_MULTICAST) {
pr_debug("destination address is multicast: ");
iphc1 |= LOWPAN_IPHC_M;
if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
pr_debug("compressed to 1 octet\n");
iphc1 |= LOWPAN_IPHC_DAM_11;
/* use last byte */
*hc06_ptr = hdr->daddr.s6_addr[15];
hc06_ptr += 1;
lowpan_push_hc_data(&hc_ptr,
&hdr->daddr.s6_addr[15], 1);
} else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
pr_debug("compressed to 4 octets\n");
iphc1 |= LOWPAN_IPHC_DAM_10;
/* second byte + the last three */
*hc06_ptr = hdr->daddr.s6_addr[1];
memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3);
hc06_ptr += 4;
lowpan_push_hc_data(&hc_ptr,
&hdr->daddr.s6_addr[1], 1);
lowpan_push_hc_data(&hc_ptr,
&hdr->daddr.s6_addr[13], 3);
} else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
pr_debug("compressed to 6 octets\n");
iphc1 |= LOWPAN_IPHC_DAM_01;
/* second byte + the last five */
*hc06_ptr = hdr->daddr.s6_addr[1];
memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5);
hc06_ptr += 6;
lowpan_push_hc_data(&hc_ptr,
&hdr->daddr.s6_addr[1], 1);
lowpan_push_hc_data(&hc_ptr,
&hdr->daddr.s6_addr[11], 5);
} else {
pr_debug("using full address\n");
iphc1 |= LOWPAN_IPHC_DAM_00;
memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16);
hc06_ptr += 16;
lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
}
} else {
/* TODO: context lookup */
if (is_addr_link_local(&hdr->daddr)) {
iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
if (addr_type & IPV6_ADDR_LINKLOCAL) {
/* TODO: context lookup */
iphc1 |= lowpan_compress_addr_64(&hc_ptr,
LOWPAN_IPHC_DAM_BIT, &hdr->daddr, _daddr);
pr_debug("dest address unicast link-local %pI6c "
"iphc1 0x%02x\n", &hdr->daddr, iphc1);
"iphc1 0x%02x\n", &hdr->daddr, iphc1);
} else {
pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16);
hc06_ptr += 16;
lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
}
}
/* UDP header compression */
if (hdr->nexthdr == UIP_PROTO_UDP)
compress_udp_header(&hc06_ptr, skb);
compress_udp_header(&hc_ptr, skb);
head[0] = iphc0;
head[1] = iphc1;
skb_pull(skb, sizeof(struct ipv6hdr));
skb_reset_transport_header(skb);
memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
memcpy(skb_push(skb, hc_ptr - head), head, hc_ptr - head);
skb_reset_network_header(skb);
pr_debug("header len %d skb %u\n", (int)(hc06_ptr - head), skb->len);
pr_debug("header len %d skb %u\n", (int)(hc_ptr - head), skb->len);
raw_dump_table(__func__, "raw skb data dump compressed",
skb->data, skb->len);
skb->data, skb->len);
return 0;
}
EXPORT_SYMBOL_GPL(lowpan_header_compress);

View file

@ -126,6 +126,6 @@ static void fc_setup(struct net_device *dev)
*/
struct net_device *alloc_fcdev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "fc%d", fc_setup);
return alloc_netdev(sizeof_priv, "fc%d", NET_NAME_UNKNOWN, fc_setup);
}
EXPORT_SYMBOL(alloc_fcdev);

View file

@ -207,7 +207,8 @@ static void fddi_setup(struct net_device *dev)
*/
struct net_device *alloc_fddidev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "fddi%d", fddi_setup);
return alloc_netdev(sizeof_priv, "fddi%d", NET_NAME_UNKNOWN,
fddi_setup);
}
EXPORT_SYMBOL(alloc_fddidev);

View file

@ -228,7 +228,8 @@ static void hippi_setup(struct net_device *dev)
struct net_device *alloc_hippi_dev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "hip%d", hippi_setup);
return alloc_netdev(sizeof_priv, "hip%d", NET_NAME_UNKNOWN,
hippi_setup);
}
EXPORT_SYMBOL(alloc_hippi_dev);

View file

@ -250,7 +250,8 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id);
}
new_dev = alloc_netdev(sizeof(struct vlan_dev_priv), name, vlan_setup);
new_dev = alloc_netdev(sizeof(struct vlan_dev_priv), name,
NET_NAME_UNKNOWN, vlan_setup);
if (new_dev == NULL)
return -ENOBUFS;
@ -324,23 +325,24 @@ static void vlan_transfer_features(struct net_device *dev,
netdev_update_features(vlandev);
}
static void __vlan_device_event(struct net_device *dev, unsigned long event)
static int __vlan_device_event(struct net_device *dev, unsigned long event)
{
int err = 0;
switch (event) {
case NETDEV_CHANGENAME:
vlan_proc_rem_dev(dev);
if (vlan_proc_add_dev(dev) < 0)
pr_warn("failed to change proc name for %s\n",
dev->name);
err = vlan_proc_add_dev(dev);
break;
case NETDEV_REGISTER:
if (vlan_proc_add_dev(dev) < 0)
pr_warn("failed to add proc entry for %s\n", dev->name);
err = vlan_proc_add_dev(dev);
break;
case NETDEV_UNREGISTER:
vlan_proc_rem_dev(dev);
break;
}
return err;
}
static int vlan_device_event(struct notifier_block *unused, unsigned long event,
@ -355,8 +357,12 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
bool last = false;
LIST_HEAD(list);
if (is_vlan_dev(dev))
__vlan_device_event(dev, event);
if (is_vlan_dev(dev)) {
int err = __vlan_device_event(dev, event);
if (err)
return notifier_from_errno(err);
}
if ((event == NETDEV_UP) &&
(dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {

View file

@ -385,6 +385,8 @@ static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSMIIREG:
case SIOCSHWTSTAMP:
case SIOCGHWTSTAMP:
if (netif_device_present(real_dev) && ops->ndo_do_ioctl)
err = ops->ndo_do_ioctl(real_dev, &ifrr, cmd);
break;

View file

@ -171,6 +171,8 @@ int vlan_proc_add_dev(struct net_device *vlandev)
struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
struct vlan_net *vn = net_generic(dev_net(vlandev), vlan_net_id);
if (!strcmp(vlandev->name, name_conf))
return -EINVAL;
vlan->dent =
proc_create_data(vlandev->name, S_IFREG|S_IRUSR|S_IWUSR,
vn->proc_vlan_dir, &vlandev_fops, vlandev);

View file

@ -959,7 +959,6 @@ static int p9_client_version(struct p9_client *c)
break;
default:
return -EINVAL;
break;
}
if (IS_ERR(req))

View file

@ -214,6 +214,7 @@ source "drivers/net/appletalk/Kconfig"
source "net/x25/Kconfig"
source "net/lapb/Kconfig"
source "net/phonet/Kconfig"
source "net/6lowpan/Kconfig"
source "net/ieee802154/Kconfig"
source "net/mac802154/Kconfig"
source "net/sched/Kconfig"

View file

@ -57,7 +57,8 @@ obj-$(CONFIG_CAIF) += caif/
ifneq ($(CONFIG_DCB),)
obj-y += dcb/
endif
obj-y += ieee802154/
obj-$(CONFIG_6LOWPAN) += 6lowpan/
obj-$(CONFIG_IEEE802154) += ieee802154/
obj-$(CONFIG_MAC802154) += mac802154/
ifeq ($(CONFIG_NET),y)

View file

@ -1805,7 +1805,7 @@ static int atalk_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
long amount = 0;
if (skb)
amount = skb->len - sizeof(struct ddpehdr);
amount = skb->len - sizeof(struct ddpehdr);
rc = put_user(amount, (int __user *)argp);
break;
}

View file

@ -39,6 +39,7 @@ static void ltalk_setup(struct net_device *dev)
struct net_device *alloc_ltalkdev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "lt%d", ltalk_setup);
return alloc_netdev(sizeof_priv, "lt%d", NET_NAME_UNKNOWN,
ltalk_setup);
}
EXPORT_SYMBOL(alloc_ltalkdev);

View file

@ -682,8 +682,8 @@ static int br2684_create(void __user *arg)
netdev = alloc_netdev(sizeof(struct br2684_dev),
ni.ifname[0] ? ni.ifname : "nas%d",
(payload == p_routed) ?
br2684_setup_routed : br2684_setup);
NET_NAME_UNKNOWN,
(payload == p_routed) ? br2684_setup_routed : br2684_setup);
if (!netdev)
return -ENOMEM;

View file

@ -520,7 +520,8 @@ static int clip_create(int number)
if (PRIV(dev)->number >= number)
number = PRIV(dev)->number + 1;
}
dev = alloc_netdev(sizeof(struct clip_priv), "", clip_setup);
dev = alloc_netdev(sizeof(struct clip_priv), "", NET_NAME_UNKNOWN,
clip_setup);
if (!dev)
return -ENOMEM;
clip_priv = PRIV(dev);

View file

@ -108,14 +108,15 @@ static int batadv_iv_ogm_orig_add_if(struct batadv_orig_node *orig_node,
int max_if_num)
{
void *data_ptr;
size_t data_size, old_size;
size_t old_size;
int ret = -ENOMEM;
spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
data_size = max_if_num * sizeof(unsigned long) * BATADV_NUM_WORDS;
old_size = (max_if_num - 1) * sizeof(unsigned long) * BATADV_NUM_WORDS;
data_ptr = kmalloc(data_size, GFP_ATOMIC);
data_ptr = kmalloc_array(max_if_num,
BATADV_NUM_WORDS * sizeof(unsigned long),
GFP_ATOMIC);
if (!data_ptr)
goto unlock;
@ -123,7 +124,7 @@ static int batadv_iv_ogm_orig_add_if(struct batadv_orig_node *orig_node,
kfree(orig_node->bat_iv.bcast_own);
orig_node->bat_iv.bcast_own = data_ptr;
data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
data_ptr = kmalloc_array(max_if_num, sizeof(uint8_t), GFP_ATOMIC);
if (!data_ptr) {
kfree(orig_node->bat_iv.bcast_own);
goto unlock;
@ -164,7 +165,7 @@ static int batadv_iv_ogm_orig_del_if(struct batadv_orig_node *orig_node,
goto free_bcast_own;
chunk_size = sizeof(unsigned long) * BATADV_NUM_WORDS;
data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC);
data_ptr = kmalloc_array(max_if_num, chunk_size, GFP_ATOMIC);
if (!data_ptr)
goto unlock;
@ -183,7 +184,7 @@ free_bcast_own:
if (max_if_num == 0)
goto free_own_sum;
data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
data_ptr = kmalloc_array(max_if_num, sizeof(uint8_t), GFP_ATOMIC);
if (!data_ptr) {
kfree(orig_node->bat_iv.bcast_own);
goto unlock;

View file

@ -537,7 +537,8 @@ batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
if (!bat_priv->orig_hash)
return NULL;
res = kmalloc(BATADV_DAT_CANDIDATES_NUM * sizeof(*res), GFP_ATOMIC);
res = kmalloc_array(BATADV_DAT_CANDIDATES_NUM, sizeof(*res),
GFP_ATOMIC);
if (!res)
return NULL;

View file

@ -128,6 +128,7 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
{
struct batadv_frag_table_entry *chain;
struct batadv_frag_list_entry *frag_entry_new = NULL, *frag_entry_curr;
struct batadv_frag_list_entry *frag_entry_last = NULL;
struct batadv_frag_packet *frag_packet;
uint8_t bucket;
uint16_t seqno, hdr_size = sizeof(struct batadv_frag_packet);
@ -180,11 +181,14 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
ret = true;
goto out;
}
/* store current entry because it could be the last in list */
frag_entry_last = frag_entry_curr;
}
/* Reached the end of the list, so insert after 'frag_entry_curr'. */
if (likely(frag_entry_curr)) {
hlist_add_after(&frag_entry_curr->list, &frag_entry_new->list);
/* Reached the end of the list, so insert after 'frag_entry_last'. */
if (likely(frag_entry_last)) {
hlist_add_after(&frag_entry_last->list, &frag_entry_new->list);
chain->size += skb->len - hdr_size;
chain->timestamp = jiffies;
ret = true;

View file

@ -46,12 +46,12 @@ struct batadv_hashtable *batadv_hash_new(uint32_t size)
if (!hash)
return NULL;
hash->table = kmalloc(sizeof(*hash->table) * size, GFP_ATOMIC);
hash->table = kmalloc_array(size, sizeof(*hash->table), GFP_ATOMIC);
if (!hash->table)
goto free_hash;
hash->list_locks = kmalloc(sizeof(*hash->list_locks) * size,
GFP_ATOMIC);
hash->list_locks = kmalloc_array(size, sizeof(*hash->list_locks),
GFP_ATOMIC);
if (!hash->list_locks)
goto free_table;

View file

@ -24,7 +24,7 @@
#define BATADV_DRIVER_DEVICE "batman-adv"
#ifndef BATADV_SOURCE_VERSION
#define BATADV_SOURCE_VERSION "2014.3.0"
#define BATADV_SOURCE_VERSION "2014.4.0"
#endif
/* B.A.T.M.A.N. parameters */
@ -238,21 +238,29 @@ enum batadv_dbg_level {
int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...)
__printf(2, 3);
#define batadv_dbg(type, bat_priv, fmt, arg...) \
/* possibly ratelimited debug output */
#define _batadv_dbg(type, bat_priv, ratelimited, fmt, arg...) \
do { \
if (atomic_read(&bat_priv->log_level) & type) \
if (atomic_read(&bat_priv->log_level) & type && \
(!ratelimited || net_ratelimit())) \
batadv_debug_log(bat_priv, fmt, ## arg);\
} \
while (0)
#else /* !CONFIG_BATMAN_ADV_DEBUG */
__printf(3, 4)
static inline void batadv_dbg(int type __always_unused,
struct batadv_priv *bat_priv __always_unused,
const char *fmt __always_unused, ...)
__printf(4, 5)
static inline void _batadv_dbg(int type __always_unused,
struct batadv_priv *bat_priv __always_unused,
int ratelimited __always_unused,
const char *fmt __always_unused, ...)
{
}
#endif
#define batadv_dbg(type, bat_priv, arg...) \
_batadv_dbg(type, bat_priv, 0, ## arg)
#define batadv_dbg_ratelimited(type, bat_priv, arg...) \
_batadv_dbg(type, bat_priv, 1, ## arg)
#define batadv_info(net_dev, fmt, arg...) \
do { \
struct net_device *_netdev = (net_dev); \

View file

@ -706,11 +706,11 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest, vid)) {
if (batadv_reroute_unicast_packet(bat_priv, unicast_packet,
ethhdr->h_dest, vid))
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);
batadv_dbg_ratelimited(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
@ -752,10 +752,10 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
*/
if (batadv_reroute_unicast_packet(bat_priv, unicast_packet,
ethhdr->h_dest, vid)) {
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);
batadv_dbg_ratelimited(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;
}

View file

@ -751,7 +751,7 @@ static int batadv_softif_init_late(struct net_device *dev)
atomic_set(&bat_priv->gw.bandwidth_down, 100);
atomic_set(&bat_priv->gw.bandwidth_up, 20);
atomic_set(&bat_priv->orig_interval, 1000);
atomic_set(&bat_priv->hop_penalty, 15);
atomic_set(&bat_priv->hop_penalty, 30);
#ifdef CONFIG_BATMAN_ADV_DEBUG
atomic_set(&bat_priv->log_level, 0);
#endif
@ -927,7 +927,7 @@ struct net_device *batadv_softif_create(const char *name)
int ret;
soft_iface = alloc_netdev(sizeof(struct batadv_priv), name,
batadv_softif_init_early);
NET_NAME_UNKNOWN, batadv_softif_init_early);
if (!soft_iface)
return NULL;

View file

@ -900,32 +900,24 @@ int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type,
bat_kobj = &bat_priv->soft_iface->dev.kobj;
uevent_env[0] = kmalloc(strlen(BATADV_UEV_TYPE_VAR) +
strlen(batadv_uev_type_str[type]) + 1,
GFP_ATOMIC);
uevent_env[0] = kasprintf(GFP_ATOMIC,
"%s%s", BATADV_UEV_TYPE_VAR,
batadv_uev_type_str[type]);
if (!uevent_env[0])
goto out;
sprintf(uevent_env[0], "%s%s", BATADV_UEV_TYPE_VAR,
batadv_uev_type_str[type]);
uevent_env[1] = kmalloc(strlen(BATADV_UEV_ACTION_VAR) +
strlen(batadv_uev_action_str[action]) + 1,
GFP_ATOMIC);
uevent_env[1] = kasprintf(GFP_ATOMIC,
"%s%s", BATADV_UEV_ACTION_VAR,
batadv_uev_action_str[action]);
if (!uevent_env[1])
goto out;
sprintf(uevent_env[1], "%s%s", BATADV_UEV_ACTION_VAR,
batadv_uev_action_str[action]);
/* If the event is DEL, ignore the data field */
if (action != BATADV_UEV_DEL) {
uevent_env[2] = kmalloc(strlen(BATADV_UEV_DATA_VAR) +
strlen(data) + 1, GFP_ATOMIC);
uevent_env[2] = kasprintf(GFP_ATOMIC,
"%s%s", BATADV_UEV_DATA_VAR, data);
if (!uevent_env[2])
goto out;
sprintf(uevent_env[2], "%s%s", BATADV_UEV_DATA_VAR, data);
}
ret = kobject_uevent_env(bat_kobj, KOBJ_CHANGE, uevent_env);

File diff suppressed because it is too large Load diff

View file

@ -1,47 +0,0 @@
/*
Copyright (c) 2013 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.
*/
#ifndef __6LOWPAN_H
#define __6LOWPAN_H
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <net/bluetooth/l2cap.h>
#if IS_ENABLED(CONFIG_BT_6LOWPAN)
int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb);
int bt_6lowpan_add_conn(struct l2cap_conn *conn);
int bt_6lowpan_del_conn(struct l2cap_conn *conn);
int bt_6lowpan_init(void);
void bt_6lowpan_cleanup(void);
#else
static int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
{
return -EOPNOTSUPP;
}
static int bt_6lowpan_add_conn(struct l2cap_conn *conn)
{
return -EOPNOTSUPP;
}
int bt_6lowpan_del_conn(struct l2cap_conn *conn)
{
return -EOPNOTSUPP;
}
static int bt_6lowpan_init(void)
{
return -EOPNOTSUPP;
}
static void bt_6lowpan_cleanup(void) { }
#endif
#endif /* __6LOWPAN_H */

View file

@ -6,7 +6,6 @@ menuconfig BT
tristate "Bluetooth subsystem support"
depends on NET && !S390
depends on RFKILL || !RFKILL
select 6LOWPAN_IPHC if BT_6LOWPAN
select CRC16
select CRYPTO
select CRYPTO_BLKCIPHER
@ -41,10 +40,10 @@ menuconfig BT
more information, see <http://www.bluez.org/>.
config BT_6LOWPAN
bool "Bluetooth 6LoWPAN support"
depends on BT && IPV6
tristate "Bluetooth 6LoWPAN support"
depends on BT && 6LOWPAN
help
IPv6 compression over Bluetooth.
IPv6 compression over Bluetooth Low Energy.
source "net/bluetooth/rfcomm/Kconfig"

View file

@ -7,10 +7,12 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm/
obj-$(CONFIG_BT_BNEP) += bnep/
obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
bluetooth_6lowpan-y := 6lowpan.o
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 amp.o
bluetooth-$(CONFIG_BT_6LOWPAN) += 6lowpan.o
subdir-ccflags-y += -D__CHECK_ENDIAN__

View file

@ -63,7 +63,7 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
msg.msg_iov = (struct iovec *) &iv;
msg.msg_iovlen = 1;
l2cap_chan_send(chan, &msg, total_len, 0);
l2cap_chan_send(chan, &msg, total_len);
kfree(cmd);
}
@ -693,18 +693,19 @@ static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state,
}
static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
unsigned long hdr_len,
unsigned long len, int nb)
{
struct sk_buff *skb;
skb = bt_skb_alloc(len, GFP_KERNEL);
skb = bt_skb_alloc(hdr_len + len, GFP_KERNEL);
if (!skb)
return ERR_PTR(-ENOMEM);
return skb;
}
static struct l2cap_ops a2mp_chan_ops = {
static const struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
.recv = a2mp_chan_recv_cb,
.close = a2mp_chan_close_cb,
@ -719,6 +720,7 @@ static struct l2cap_ops a2mp_chan_ops = {
.resume = l2cap_chan_no_resume,
.set_shutdown = l2cap_chan_no_set_shutdown,
.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
.memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
};
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)

View file

@ -639,7 +639,7 @@ static int bt_seq_show(struct seq_file *seq, void *v)
return 0;
}
static struct seq_operations bt_seq_ops = {
static const struct seq_operations bt_seq_ops = {
.start = bt_seq_start,
.next = bt_seq_next,
.stop = bt_seq_stop,

View file

@ -113,8 +113,9 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
{
bdaddr_t *dst = &mgr->l2cap_conn->hcon->dst;
struct hci_conn *hcon;
u8 role = out ? HCI_ROLE_MASTER : HCI_ROLE_SLAVE;
hcon = hci_conn_add(hdev, AMP_LINK, dst);
hcon = hci_conn_add(hdev, AMP_LINK, dst, role);
if (!hcon)
return NULL;
@ -125,7 +126,6 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
hcon->handle = __next_handle(mgr);
hcon->remote_id = remote_id;
hcon->amp_mgr = amp_mgr_get(mgr);
hcon->out = out;
return hcon;
}
@ -133,8 +133,8 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
/* 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;
int ret;
if (!ksize)
return -EINVAL;

View file

@ -538,8 +538,9 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
/* session struct allocated as private part of net_device */
dev = alloc_netdev(sizeof(struct bnep_session),
(*req->device) ? req->device : "bnep%d",
bnep_net_setup);
(*req->device) ? req->device : "bnep%d",
NET_NAME_UNKNOWN,
bnep_net_setup);
if (!dev)
return -ENOMEM;

View file

@ -362,12 +362,6 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
CAPIMSG_SETCONTROL(skb->data, contr);
}
if (!ctrl) {
BT_ERR("Can't find controller %d for message", session->num);
kfree_skb(skb);
return;
}
capi_ctr_handle_message(ctrl, appl, skb);
}

View file

@ -66,8 +66,7 @@ static void hci_acl_create_connection(struct hci_conn *conn)
conn->state = BT_CONNECT;
conn->out = true;
conn->link_mode = HCI_LM_MASTER;
conn->role = HCI_ROLE_MASTER;
conn->attempt++;
@ -136,7 +135,7 @@ void hci_disconnect(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)
static void hci_amp_disconn(struct hci_conn *conn)
{
struct hci_cp_disconn_phy_link cp;
@ -145,7 +144,7 @@ static void hci_amp_disconn(struct hci_conn *conn, __u8 reason)
conn->state = BT_DISCONN;
cp.phy_handle = HCI_PHY_HANDLE(conn->handle);
cp.reason = reason;
cp.reason = hci_proto_disconn_ind(conn);
hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHY_LINK,
sizeof(cp), &cp);
}
@ -213,14 +212,26 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
return true;
}
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
u16 latency, u16 to_multiplier)
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
u16 to_multiplier)
{
struct hci_cp_le_conn_update cp;
struct hci_dev *hdev = conn->hdev;
struct hci_conn_params *params;
struct hci_cp_le_conn_update cp;
hci_dev_lock(hdev);
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
if (params) {
params->conn_min_interval = min;
params->conn_max_interval = max;
params->conn_latency = latency;
params->supervision_timeout = to_multiplier;
}
hci_dev_unlock(hdev);
memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(conn->handle);
cp.conn_interval_min = cpu_to_le16(min);
cp.conn_interval_max = cpu_to_le16(max);
@ -230,6 +241,11 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
cp.max_ce_len = cpu_to_le16(0x0000);
hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp);
if (params)
return 0x01;
return 0x00;
}
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
@ -271,20 +287,6 @@ 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 AMP_LINK:
hci_amp_disconn(conn, reason);
break;
default:
hci_disconnect(conn, reason);
break;
}
}
static void hci_conn_timeout(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn,
@ -319,7 +321,31 @@ static void hci_conn_timeout(struct work_struct *work)
break;
case BT_CONFIG:
case BT_CONNECTED:
hci_conn_disconnect(conn);
if (conn->type == AMP_LINK) {
hci_amp_disconn(conn);
} else {
__u8 reason = hci_proto_disconn_ind(conn);
/* When we are master of an established connection
* and it enters the disconnect timeout, then go
* ahead and try to read the current clock offset.
*
* Processing of the result is done within the
* event handling and hci_clock_offset_evt function.
*/
if (conn->type == ACL_LINK &&
conn->role == HCI_ROLE_MASTER) {
struct hci_dev *hdev = conn->hdev;
struct hci_cp_read_clock_offset cp;
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET,
sizeof(cp), &cp);
}
hci_disconnect(conn, reason);
}
break;
default:
conn->state = BT_CLOSED;
@ -336,9 +362,6 @@ static void hci_conn_idle(struct work_struct *work)
BT_DBG("hcon %p mode %d", conn, conn->mode);
if (test_bit(HCI_RAW, &hdev->flags))
return;
if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn))
return;
@ -398,13 +421,14 @@ static void le_conn_timeout(struct work_struct *work)
hci_le_create_connection_cancel(conn);
}
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
u8 role)
{
struct hci_conn *conn;
BT_DBG("%s dst %pMR", hdev->name, dst);
conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL);
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
if (!conn)
return NULL;
@ -412,6 +436,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
bacpy(&conn->src, &hdev->bdaddr);
conn->hdev = hdev;
conn->type = type;
conn->role = role;
conn->mode = HCI_CM_ACTIVE;
conn->state = BT_OPEN;
conn->auth_type = HCI_AT_GENERAL_BONDING;
@ -424,6 +449,9 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
if (conn->role == HCI_ROLE_MASTER)
conn->out = true;
switch (type) {
case ACL_LINK:
conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
@ -529,7 +557,6 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
list_for_each_entry(d, &hci_dev_list, list) {
if (!test_bit(HCI_UP, &d->flags) ||
test_bit(HCI_RAW, &d->flags) ||
test_bit(HCI_USER_CHANNEL, &d->dev_flags) ||
d->dev_type != HCI_BREDR)
continue;
@ -627,7 +654,8 @@ static void hci_req_add_le_create_conn(struct hci_request *req,
cp.own_address_type = own_addr_type;
cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval);
cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval);
cp.supervision_timeout = cpu_to_le16(0x002a);
cp.conn_latency = cpu_to_le16(conn->le_conn_latency);
cp.supervision_timeout = cpu_to_le16(conn->le_supv_timeout);
cp.min_ce_len = cpu_to_le16(0x0000);
cp.max_ce_len = cpu_to_le16(0x0000);
@ -644,15 +672,12 @@ static void hci_req_directed_advertising(struct hci_request *req,
u8 own_addr_type;
u8 enable;
enable = 0x00;
hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
/* Clear the HCI_ADVERTISING bit temporarily so that the
/* Clear the HCI_LE_ADV bit temporarily so that the
* hci_update_random_address knows that it's safe to go ahead
* and write a new random address. The flag will be set back on
* as soon as the SET_ADV_ENABLE HCI command completes.
*/
clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
clear_bit(HCI_LE_ADV, &hdev->dev_flags);
/* Set require_privacy to false so that the remote device has a
* chance of identifying us.
@ -676,7 +701,8 @@ static void hci_req_directed_advertising(struct hci_request *req,
}
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level, u8 auth_type)
u8 dst_type, u8 sec_level, u16 conn_timeout,
u8 role)
{
struct hci_conn_params *params;
struct hci_conn *conn;
@ -696,7 +722,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
if (conn) {
conn->pending_sec_level = sec_level;
conn->auth_type = auth_type;
goto done;
}
@ -726,32 +751,56 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
dst_type = ADDR_LE_DEV_RANDOM;
}
conn = hci_conn_add(hdev, LE_LINK, dst);
conn = hci_conn_add(hdev, LE_LINK, dst, role);
if (!conn)
return ERR_PTR(-ENOMEM);
conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->pending_sec_level = sec_level;
conn->auth_type = auth_type;
conn->conn_timeout = conn_timeout;
hci_req_init(&req, hdev);
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
/* Disable advertising if we're active. For master role
* connections most controllers will refuse to connect if
* advertising is enabled, and for slave role connections we
* anyway have to disable it in order to start directed
* advertising.
*/
if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
u8 enable = 0x00;
hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
&enable);
}
/* If requested to connect as slave use directed advertising */
if (conn->role == HCI_ROLE_SLAVE) {
/* If we're active scanning most controllers are unable
* to initiate advertising. Simply reject the attempt.
*/
if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
hdev->le_scan_type == LE_SCAN_ACTIVE) {
skb_queue_purge(&req.cmd_q);
hci_conn_del(conn);
return ERR_PTR(-EBUSY);
}
hci_req_directed_advertising(&req, conn);
goto create_conn;
}
conn->out = true;
conn->link_mode |= HCI_LM_MASTER;
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
if (params) {
conn->le_conn_min_interval = params->conn_min_interval;
conn->le_conn_max_interval = params->conn_max_interval;
conn->le_conn_latency = params->conn_latency;
conn->le_supv_timeout = params->supervision_timeout;
} else {
conn->le_conn_min_interval = hdev->le_conn_min_interval;
conn->le_conn_max_interval = hdev->le_conn_max_interval;
conn->le_conn_latency = hdev->le_conn_latency;
conn->le_supv_timeout = hdev->le_supv_timeout;
}
/* If controller is scanning, we stop it since some controllers are
@ -785,11 +834,11 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
struct hci_conn *acl;
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
return ERR_PTR(-ENOTSUPP);
return ERR_PTR(-EOPNOTSUPP);
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (!acl) {
acl = hci_conn_add(hdev, ACL_LINK, dst);
acl = hci_conn_add(hdev, ACL_LINK, dst, HCI_ROLE_MASTER);
if (!acl)
return ERR_PTR(-ENOMEM);
}
@ -818,7 +867,7 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
sco = hci_conn_hash_lookup_ba(hdev, type, dst);
if (!sco) {
sco = hci_conn_add(hdev, type, dst);
sco = hci_conn_add(hdev, type, dst, HCI_ROLE_MASTER);
if (!sco) {
hci_conn_drop(acl);
return ERR_PTR(-ENOMEM);
@ -865,7 +914,8 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
return 0;
}
if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT))
if (hci_conn_ssp_enabled(conn) &&
!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
return 0;
return 1;
@ -881,7 +931,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
if (sec_level > conn->sec_level)
conn->pending_sec_level = sec_level;
else if (conn->link_mode & HCI_LM_AUTH)
else if (test_bit(HCI_CONN_AUTH, &conn->flags))
return 1;
/* Make sure we preserve an existing MITM requirement*/
@ -899,7 +949,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
/* If we're already encrypted set the REAUTH_PEND flag,
* otherwise set the ENCRYPT_PEND.
*/
if (conn->link_mode & HCI_LM_ENCRYPT)
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
set_bit(HCI_CONN_REAUTH_PEND, &conn->flags);
else
set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
@ -923,7 +973,8 @@ static void hci_conn_encrypt(struct hci_conn *conn)
}
/* Enable security */
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
bool initiator)
{
BT_DBG("hcon %p", conn);
@ -940,7 +991,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
return 1;
/* For other security levels we need the link key. */
if (!(conn->link_mode & HCI_LM_AUTH))
if (!test_bit(HCI_CONN_AUTH, &conn->flags))
goto auth;
/* An authenticated FIPS approved combination key has sufficient
@ -976,11 +1027,14 @@ auth:
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
return 0;
if (initiator)
set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
if (!hci_conn_auth(conn, sec_level, auth_type))
return 0;
encrypt:
if (conn->link_mode & HCI_LM_ENCRYPT)
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
return 1;
hci_conn_encrypt(conn);
@ -1027,7 +1081,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
{
BT_DBG("hcon %p", conn);
if (!role && conn->link_mode & HCI_LM_MASTER)
if (role == conn->role)
return 1;
if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) {
@ -1048,9 +1102,6 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
BT_DBG("hcon %p mode %d", conn, conn->mode);
if (test_bit(HCI_RAW, &hdev->flags))
return;
if (conn->mode != HCI_CM_SNIFF)
goto timer;
@ -1101,6 +1152,28 @@ void hci_conn_check_pending(struct hci_dev *hdev)
hci_dev_unlock(hdev);
}
static u32 get_link_mode(struct hci_conn *conn)
{
u32 link_mode = 0;
if (conn->role == HCI_ROLE_MASTER)
link_mode |= HCI_LM_MASTER;
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
link_mode |= HCI_LM_ENCRYPT;
if (test_bit(HCI_CONN_AUTH, &conn->flags))
link_mode |= HCI_LM_AUTH;
if (test_bit(HCI_CONN_SECURE, &conn->flags))
link_mode |= HCI_LM_SECURE;
if (test_bit(HCI_CONN_FIPS, &conn->flags))
link_mode |= HCI_LM_FIPS;
return link_mode;
}
int hci_get_conn_list(void __user *arg)
{
struct hci_conn *c;
@ -1136,7 +1209,7 @@ int hci_get_conn_list(void __user *arg)
(ci + n)->type = c->type;
(ci + n)->out = c->out;
(ci + n)->state = c->state;
(ci + n)->link_mode = c->link_mode;
(ci + n)->link_mode = get_link_mode(c);
if (++n >= req.conn_num)
break;
}
@ -1172,7 +1245,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg)
ci.type = conn->type;
ci.out = conn->out;
ci.state = conn->state;
ci.link_mode = conn->link_mode;
ci.link_mode = get_link_mode(conn);
}
hci_dev_unlock(hdev);
@ -1209,7 +1282,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
BT_DBG("%s hcon %p", hdev->name, conn);
chan = kzalloc(sizeof(struct hci_chan), GFP_KERNEL);
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
if (!chan)
return NULL;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -35,13 +35,32 @@ static atomic_t monitor_promisc = ATOMIC_INIT(0);
/* ----- HCI socket interface ----- */
/* Socket info */
#define hci_pi(sk) ((struct hci_pinfo *) sk)
struct hci_pinfo {
struct bt_sock bt;
struct hci_dev *hdev;
struct hci_filter filter;
__u32 cmsg_mask;
unsigned short channel;
};
static inline int hci_test_bit(int nr, void *addr)
{
return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
}
/* Security filter */
static struct hci_sec_filter hci_sec_filter = {
#define HCI_SFLT_MAX_OGF 5
struct hci_sec_filter {
__u32 type_mask;
__u32 event_mask[2];
__u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
};
static const struct hci_sec_filter hci_sec_filter = {
/* Packet types */
0x10,
/* Events */
@ -481,7 +500,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
hci_dev_lock(hdev);
err = hci_blacklist_add(hdev, &bdaddr, BDADDR_BREDR);
err = hci_bdaddr_list_add(&hdev->blacklist, &bdaddr, BDADDR_BREDR);
hci_dev_unlock(hdev);
@ -498,7 +517,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
hci_dev_lock(hdev);
err = hci_blacklist_del(hdev, &bdaddr, BDADDR_BREDR);
err = hci_bdaddr_list_del(&hdev->blacklist, &bdaddr, BDADDR_BREDR);
hci_dev_unlock(hdev);
@ -517,6 +536,9 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
return -EBUSY;
if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
return -EOPNOTSUPP;
if (hdev->dev_type != HCI_BREDR)
return -EOPNOTSUPP;
@ -690,7 +712,8 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
if (test_bit(HCI_UP, &hdev->flags) ||
test_bit(HCI_INIT, &hdev->flags) ||
test_bit(HCI_SETUP, &hdev->dev_flags)) {
test_bit(HCI_SETUP, &hdev->dev_flags) ||
test_bit(HCI_CONFIG, &hdev->dev_flags)) {
err = -EBUSY;
hci_dev_put(hdev);
goto done;
@ -960,7 +983,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
goto drop;
}
if (test_bit(HCI_RAW, &hdev->flags) || (ogf == 0x3f)) {
if (ogf == 0x3f) {
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
} else {

View file

@ -154,7 +154,7 @@ static int hidp_input_event(struct input_dev *dev, unsigned int type,
(!!test_bit(LED_COMPOSE, dev->led) << 3) |
(!!test_bit(LED_SCROLLL, dev->led) << 2) |
(!!test_bit(LED_CAPSL, dev->led) << 1) |
(!!test_bit(LED_NUML, dev->led));
(!!test_bit(LED_NUML, dev->led) << 0);
if (session->leds == newleds)
return 0;

View file

@ -40,14 +40,13 @@
#include "smp.h"
#include "a2mp.h"
#include "amp.h"
#include "6lowpan.h"
#define LE_FLOWCTL_MAX_CREDITS 65535
bool disable_ertm;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, };
static u8 l2cap_fixed_chan[8] = { L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS, };
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
@ -205,6 +204,7 @@ done:
write_unlock(&chan_list_lock);
return err;
}
EXPORT_SYMBOL_GPL(l2cap_add_psm);
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
{
@ -437,6 +437,7 @@ struct l2cap_chan *l2cap_chan_create(void)
return chan;
}
EXPORT_SYMBOL_GPL(l2cap_chan_create);
static void l2cap_chan_destroy(struct kref *kref)
{
@ -464,6 +465,7 @@ void l2cap_chan_put(struct l2cap_chan *c)
kref_put(&c->kref, l2cap_chan_destroy);
}
EXPORT_SYMBOL_GPL(l2cap_chan_put);
void l2cap_chan_set_defaults(struct l2cap_chan *chan)
{
@ -482,6 +484,7 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
}
EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults);
static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
{
@ -614,6 +617,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
return;
}
EXPORT_SYMBOL_GPL(l2cap_chan_del);
void l2cap_conn_update_id_addr(struct hci_conn *hcon)
{
@ -717,6 +721,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
break;
}
}
EXPORT_SYMBOL(l2cap_chan_close);
static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
{
@ -770,7 +775,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
}
/* Service level security */
int l2cap_chan_check_security(struct l2cap_chan *chan)
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator)
{
struct l2cap_conn *conn = chan->conn;
__u8 auth_type;
@ -780,7 +785,8 @@ int l2cap_chan_check_security(struct l2cap_chan *chan)
auth_type = l2cap_get_auth_type(chan);
return hci_conn_security(conn->hcon, chan->sec_level, auth_type);
return hci_conn_security(conn->hcon, chan->sec_level, auth_type,
initiator);
}
static u8 l2cap_get_ident(struct l2cap_conn *conn)
@ -793,14 +799,14 @@ static u8 l2cap_get_ident(struct l2cap_conn *conn)
* 200 - 254 are used by utilities like l2ping, etc.
*/
spin_lock(&conn->lock);
mutex_lock(&conn->ident_lock);
if (++conn->tx_ident > 128)
conn->tx_ident = 1;
id = conn->tx_ident;
spin_unlock(&conn->lock);
mutex_unlock(&conn->ident_lock);
return id;
}
@ -1273,7 +1279,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
return;
if (l2cap_chan_check_security(chan) &&
if (l2cap_chan_check_security(chan, true) &&
__l2cap_no_conn_pending(chan)) {
l2cap_start_connection(chan);
}
@ -1352,7 +1358,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
}
if (chan->state == BT_CONNECT) {
if (!l2cap_chan_check_security(chan) ||
if (!l2cap_chan_check_security(chan, true) ||
!__l2cap_no_conn_pending(chan)) {
l2cap_chan_unlock(chan);
continue;
@ -1374,7 +1380,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
if (l2cap_chan_check_security(chan)) {
if (l2cap_chan_check_security(chan, false)) {
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
@ -1455,13 +1461,12 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
{
struct hci_conn *hcon = conn->hcon;
struct hci_dev *hdev = hcon->hdev;
struct l2cap_chan *chan, *pchan;
u8 dst_type;
BT_DBG("");
bt_6lowpan_add_conn(conn);
/* Check if we have socket listening on cid */
pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
&hcon->src, &hcon->dst);
@ -1475,9 +1480,28 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
dst_type = bdaddr_type(hcon, hcon->dst_type);
/* If device is blocked, do not create a channel for it */
if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type))
if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type))
return;
/* For LE slave connections, make sure the connection interval
* is in the range of the minium and maximum interval that has
* been configured for this connection. If not, then trigger
* the connection update procedure.
*/
if (hcon->role == HCI_ROLE_SLAVE &&
(hcon->le_conn_interval < hcon->le_conn_min_interval ||
hcon->le_conn_interval > hcon->le_conn_max_interval)) {
struct l2cap_conn_param_update_req req;
req.min = cpu_to_le16(hcon->le_conn_min_interval);
req.max = cpu_to_le16(hcon->le_conn_max_interval);
req.latency = cpu_to_le16(hcon->le_conn_latency);
req.to_multiplier = cpu_to_le16(hcon->le_supv_timeout);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_CONN_PARAM_UPDATE_REQ, sizeof(req), &req);
}
l2cap_chan_lock(pchan);
chan = pchan->ops->new_connection(pchan);
@ -2118,7 +2142,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
struct sk_buff **frag;
int sent = 0;
if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count))
if (chan->ops->memcpy_fromiovec(chan, skb_put(skb, count),
msg->msg_iov, count))
return -EFAULT;
sent += count;
@ -2131,18 +2156,17 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
count = min_t(unsigned int, conn->mtu, len);
tmp = chan->ops->alloc_skb(chan, count,
tmp = chan->ops->alloc_skb(chan, 0, count,
msg->msg_flags & MSG_DONTWAIT);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
*frag = tmp;
if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))
if (chan->ops->memcpy_fromiovec(chan, skb_put(*frag, count),
msg->msg_iov, count))
return -EFAULT;
(*frag)->priority = skb->priority;
sent += count;
len -= count;
@ -2156,26 +2180,23 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
}
static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 priority)
struct msghdr *msg, size_t len)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
struct l2cap_hdr *lh;
BT_DBG("chan %p psm 0x%2.2x len %zu priority %u", chan,
__le16_to_cpu(chan->psm), len, priority);
BT_DBG("chan %p psm 0x%2.2x len %zu", chan,
__le16_to_cpu(chan->psm), len);
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = chan->ops->alloc_skb(chan, count + hlen,
skb = chan->ops->alloc_skb(chan, hlen, count,
msg->msg_flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
skb->priority = priority;
/* Create L2CAP header */
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
@ -2191,8 +2212,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
}
static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 priority)
struct msghdr *msg, size_t len)
{
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
@ -2203,13 +2223,11 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
skb = chan->ops->alloc_skb(chan, count + L2CAP_HDR_SIZE,
skb = chan->ops->alloc_skb(chan, L2CAP_HDR_SIZE, count,
msg->msg_flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
skb->priority = priority;
/* Create L2CAP header */
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
@ -2247,7 +2265,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = chan->ops->alloc_skb(chan, count + hlen,
skb = chan->ops->alloc_skb(chan, hlen, count,
msg->msg_flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
@ -2368,7 +2386,7 @@ static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
count = min_t(unsigned int, (conn->mtu - hlen), len);
skb = chan->ops->alloc_skb(chan, count + hlen,
skb = chan->ops->alloc_skb(chan, hlen, count,
msg->msg_flags & MSG_DONTWAIT);
if (IS_ERR(skb))
return skb;
@ -2430,8 +2448,7 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
return 0;
}
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
u32 priority)
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
{
struct sk_buff *skb;
int err;
@ -2442,7 +2459,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
/* Connectionless channel */
if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
skb = l2cap_create_connless_pdu(chan, msg, len, priority);
skb = l2cap_create_connless_pdu(chan, msg, len);
if (IS_ERR(skb))
return PTR_ERR(skb);
@ -2499,7 +2516,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
return -EMSGSIZE;
/* Create a basic PDU */
skb = l2cap_create_basic_pdu(chan, msg, len, priority);
skb = l2cap_create_basic_pdu(chan, msg, len);
if (IS_ERR(skb))
return PTR_ERR(skb);
@ -2562,6 +2579,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
return err;
}
EXPORT_SYMBOL_GPL(l2cap_chan_send);
static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq)
{
@ -3217,6 +3235,9 @@ done:
switch (chan->mode) {
case L2CAP_MODE_BASIC:
if (disable_ertm)
break;
if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) &&
!(chan->conn->feat_mask & L2CAP_FEAT_STREAMING))
break;
@ -3829,7 +3850,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
chan->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_chan_check_security(chan)) {
if (l2cap_chan_check_security(chan, false)) {
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
@ -5197,27 +5218,6 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
return 0;
}
static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency,
u16 to_multiplier)
{
u16 max_latency;
if (min > max || min < 6 || max > 3200)
return -EINVAL;
if (to_multiplier < 10 || to_multiplier > 3200)
return -EINVAL;
if (max >= to_multiplier * 8)
return -EINVAL;
max_latency = (to_multiplier * 8 / max) - 1;
if (latency > 499 || latency > max_latency)
return -EINVAL;
return 0;
}
static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, u8 *data)
@ -5228,7 +5228,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
u16 min, max, latency, to_multiplier;
int err;
if (!(hcon->link_mode & HCI_LM_MASTER))
if (hcon->role != HCI_ROLE_MASTER)
return -EINVAL;
if (cmd_len != sizeof(struct l2cap_conn_param_update_req))
@ -5245,7 +5245,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
memset(&rsp, 0, sizeof(rsp));
err = l2cap_check_conn_param(min, max, latency, to_multiplier);
err = hci_check_conn_params(min, max, latency, to_multiplier);
if (err)
rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED);
else
@ -5254,8 +5254,16 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP,
sizeof(rsp), &rsp);
if (!err)
hci_le_conn_update(hcon, min, max, latency, to_multiplier);
if (!err) {
u8 store_hint;
store_hint = hci_le_conn_update(hcon, min, max, latency,
to_multiplier);
mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type,
store_hint, min, max, latency,
to_multiplier);
}
return 0;
}
@ -6879,9 +6887,6 @@ static void l2cap_att_channel(struct l2cap_conn *conn,
BT_DBG("chan %p, len %d", chan, skb->len);
if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type))
goto drop;
if (chan->imtu < skb->len)
goto drop;
@ -6914,6 +6919,16 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
return;
}
/* Since we can't actively block incoming LE connections we must
* at least ensure that we ignore incoming data from them.
*/
if (hcon->type == LE_LINK &&
hci_bdaddr_list_lookup(&hcon->hdev->blacklist, &hcon->dst,
bdaddr_type(hcon, hcon->dst_type))) {
kfree_skb(skb);
return;
}
BT_DBG("len %d, cid 0x%4.4x", len, cid);
switch (cid) {
@ -6940,10 +6955,6 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
l2cap_conn_del(conn->hcon, EACCES);
break;
case L2CAP_FC_6LOWPAN:
bt_6lowpan_recv(conn, skb);
break;
default:
l2cap_data_channel(conn, cid, skb);
break;
@ -6974,7 +6985,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
if (!hchan)
return NULL;
conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
if (!conn) {
hci_chan_del(hchan);
return NULL;
@ -7006,7 +7017,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
conn->hs_enabled = test_bit(HCI_HS_ENABLED,
&hcon->hdev->dev_flags);
spin_lock_init(&conn->lock);
mutex_init(&conn->ident_lock);
mutex_init(&conn->chan_lock);
INIT_LIST_HEAD(&conn->chan_l);
@ -7042,7 +7053,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
struct l2cap_conn *conn;
struct hci_conn *hcon;
struct hci_dev *hdev;
__u8 auth_type;
int err;
BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
@ -7084,7 +7094,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
break;
/* fall through */
default:
err = -ENOTSUPP;
err = -EOPNOTSUPP;
goto done;
}
@ -7118,9 +7128,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
chan->psm = psm;
chan->dcid = cid;
auth_type = l2cap_get_auth_type(chan);
if (bdaddr_type_is_le(dst_type)) {
u8 role;
/* Convert from L2CAP channel address type to HCI address type
*/
if (dst_type == BDADDR_LE_PUBLIC)
@ -7128,9 +7138,15 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
else
dst_type = ADDR_LE_DEV_RANDOM;
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
role = HCI_ROLE_SLAVE;
else
role = HCI_ROLE_MASTER;
hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level,
auth_type);
HCI_LE_CONN_TIMEOUT, role);
} else {
u8 auth_type = l2cap_get_auth_type(chan);
hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type);
}
@ -7176,7 +7192,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan);
if (l2cap_chan_check_security(chan))
if (l2cap_chan_check_security(chan, true))
l2cap_state_change(chan, BT_CONNECTED);
} else
l2cap_do_start(chan);
@ -7190,6 +7206,7 @@ done:
hci_dev_put(hdev);
return err;
}
EXPORT_SYMBOL_GPL(l2cap_chan_connect);
/* ---- L2CAP interface with lower layer (HCI) ---- */
@ -7252,8 +7269,6 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{
BT_DBG("hcon %p reason %d", hcon, reason);
bt_6lowpan_del_conn(hcon->l2cap_data);
l2cap_conn_del(hcon, bt_to_errno(reason));
}
@ -7536,14 +7551,11 @@ int __init l2cap_init(void)
debugfs_create_u16("l2cap_le_default_mps", 0644, bt_debugfs,
&le_default_mps);
bt_6lowpan_init();
return 0;
}
void l2cap_exit(void)
{
bt_6lowpan_cleanup();
debugfs_remove(l2cap_debugfs);
l2cap_cleanup_sockets();
}

View file

@ -279,7 +279,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
break;
/* fall through */
default:
err = -ENOTSUPP;
err = -EOPNOTSUPP;
goto done;
}
@ -361,7 +361,8 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
BT_DBG("sock %p, sk %p", sock, sk);
if (peer && sk->sk_state != BT_CONNECTED &&
sk->sk_state != BT_CONNECT && sk->sk_state != BT_CONNECT2)
sk->sk_state != BT_CONNECT && sk->sk_state != BT_CONNECT2 &&
sk->sk_state != BT_CONFIG)
return -ENOTCONN;
memset(la, 0, sizeof(struct sockaddr_l2));
@ -796,7 +797,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
} else if ((sk->sk_state == BT_CONNECT2 &&
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) ||
sk->sk_state == BT_CONNECTED) {
if (!l2cap_chan_check_security(chan))
if (!l2cap_chan_check_security(chan, true))
set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
else
sk->sk_state_change(sk);
@ -964,7 +965,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
return err;
l2cap_chan_lock(chan);
err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
err = l2cap_chan_send(chan, msg, len);
l2cap_chan_unlock(chan);
return err;
@ -1111,7 +1112,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
l2cap_chan_close(chan, 0);
lock_sock(sk);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
!(current->flags & PF_EXITING))
err = bt_sock_wait_state(sk, BT_CLOSED,
sk->sk_lingertime);
}
@ -1292,6 +1294,7 @@ static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
}
static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
unsigned long hdr_len,
unsigned long len, int nb)
{
struct sock *sk = chan->data;
@ -1299,17 +1302,26 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
int err;
l2cap_chan_unlock(chan);
skb = bt_skb_send_alloc(sk, len, nb, &err);
skb = bt_skb_send_alloc(sk, hdr_len + len, nb, &err);
l2cap_chan_lock(chan);
if (!skb)
return ERR_PTR(err);
skb->priority = sk->sk_priority;
bt_cb(skb)->chan = chan;
return skb;
}
static int l2cap_sock_memcpy_fromiovec_cb(struct l2cap_chan *chan,
unsigned char *kdata,
struct iovec *iov, int len)
{
return memcpy_fromiovec(kdata, iov, len);
}
static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
{
struct sock *sk = chan->data;
@ -1375,20 +1387,21 @@ static void l2cap_sock_suspend_cb(struct l2cap_chan *chan)
sk->sk_state_change(sk);
}
static struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
.recv = l2cap_sock_recv_cb,
.close = l2cap_sock_close_cb,
.teardown = l2cap_sock_teardown_cb,
.state_change = l2cap_sock_state_change_cb,
.ready = l2cap_sock_ready_cb,
.defer = l2cap_sock_defer_cb,
.resume = l2cap_sock_resume_cb,
.suspend = l2cap_sock_suspend_cb,
.set_shutdown = l2cap_sock_set_shutdown_cb,
.get_sndtimeo = l2cap_sock_get_sndtimeo_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
static const struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
.recv = l2cap_sock_recv_cb,
.close = l2cap_sock_close_cb,
.teardown = l2cap_sock_teardown_cb,
.state_change = l2cap_sock_state_change_cb,
.ready = l2cap_sock_ready_cb,
.defer = l2cap_sock_defer_cb,
.resume = l2cap_sock_resume_cb,
.suspend = l2cap_sock_suspend_cb,
.set_shutdown = l2cap_sock_set_shutdown_cb,
.get_sndtimeo = l2cap_sock_get_sndtimeo_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
.memcpy_fromiovec = l2cap_sock_memcpy_fromiovec_cb,
};
static void l2cap_sock_destruct(struct sock *sk)

File diff suppressed because it is too large Load diff

View file

@ -227,7 +227,8 @@ static int rfcomm_check_security(struct rfcomm_dlc *d)
break;
}
return hci_conn_security(conn->hcon, d->sec_level, auth_type);
return hci_conn_security(conn->hcon, d->sec_level, auth_type,
d->out);
}
static void rfcomm_session_timeout(unsigned long arg)
@ -1909,10 +1910,13 @@ static struct rfcomm_session *rfcomm_process_rx(struct rfcomm_session *s)
/* Get data directly from socket receive queue without copying it. */
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
skb_orphan(skb);
if (!skb_linearize(skb))
if (!skb_linearize(skb)) {
s = rfcomm_recv_frame(s, skb);
else
if (!s)
break;
} else {
kfree_skb(skb);
}
}
if (s && (sk->sk_state == BT_CLOSED))

View file

@ -918,7 +918,8 @@ static int rfcomm_sock_shutdown(struct socket *sock, int how)
sk->sk_shutdown = SHUTDOWN_MASK;
__rfcomm_sock_close(sk);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
!(current->flags & PF_EXITING))
err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
}
release_sock(sk);

View file

@ -40,13 +40,38 @@ static struct bt_sock_list sco_sk_list = {
.lock = __RW_LOCK_UNLOCKED(sco_sk_list.lock)
};
static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
static void sco_chan_del(struct sock *sk, int err);
/* ---- SCO connections ---- */
struct sco_conn {
struct hci_conn *hcon;
spinlock_t lock;
struct sock *sk;
unsigned int mtu;
};
#define sco_conn_lock(c) spin_lock(&c->lock);
#define sco_conn_unlock(c) spin_unlock(&c->lock);
static void sco_sock_close(struct sock *sk);
static void sco_sock_kill(struct sock *sk);
/* ----- SCO socket info ----- */
#define sco_pi(sk) ((struct sco_pinfo *) sk)
struct sco_pinfo {
struct bt_sock bt;
bdaddr_t src;
bdaddr_t dst;
__u32 flags;
__u16 setting;
struct sco_conn *conn;
};
/* ---- SCO timers ---- */
#define SCO_CONN_TIMEOUT (HZ * 40)
#define SCO_DISCONN_TIMEOUT (HZ * 2)
static void sco_sock_timeout(unsigned long arg)
{
struct sock *sk = (struct sock *) arg;
@ -102,13 +127,31 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon)
return conn;
}
static struct sock *sco_chan_get(struct sco_conn *conn)
/* Delete channel.
* Must be called on the locked socket. */
static void sco_chan_del(struct sock *sk, int err)
{
struct sock *sk = NULL;
sco_conn_lock(conn);
sk = conn->sk;
sco_conn_unlock(conn);
return sk;
struct sco_conn *conn;
conn = sco_pi(sk)->conn;
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_drop(conn->hcon);
}
sk->sk_state = BT_CLOSED;
sk->sk_err = err;
sk->sk_state_change(sk);
sock_set_flag(sk, SOCK_ZAPPED);
}
static int sco_conn_del(struct hci_conn *hcon, int err)
@ -122,7 +165,10 @@ static int sco_conn_del(struct hci_conn *hcon, int err)
BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
/* Kill socket */
sk = sco_chan_get(conn);
sco_conn_lock(conn);
sk = conn->sk;
sco_conn_unlock(conn);
if (sk) {
bh_lock_sock(sk);
sco_sock_clear_timer(sk);
@ -136,6 +182,17 @@ static int sco_conn_del(struct hci_conn *hcon, int err)
return 0;
}
static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
{
BT_DBG("conn %p", conn);
sco_pi(sk)->conn = conn;
conn->sk = sk;
if (parent)
bt_accept_enqueue(parent, sk);
}
static int sco_chan_add(struct sco_conn *conn, struct sock *sk,
struct sock *parent)
{
@ -240,7 +297,11 @@ static int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
static void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
{
struct sock *sk = sco_chan_get(conn);
struct sock *sk;
sco_conn_lock(conn);
sk = conn->sk;
sco_conn_unlock(conn);
if (!sk)
goto drop;
@ -909,7 +970,8 @@ static int sco_sock_shutdown(struct socket *sock, int how)
sco_sock_clear_timer(sk);
__sco_sock_close(sk);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
!(current->flags & PF_EXITING))
err = bt_sock_wait_state(sk, BT_CLOSED,
sk->sk_lingertime);
}
@ -929,7 +991,8 @@ static int sco_sock_release(struct socket *sock)
sco_sock_close(sk);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) {
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
!(current->flags & PF_EXITING)) {
lock_sock(sk);
err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
release_sock(sk);
@ -940,44 +1003,6 @@ static int sco_sock_release(struct socket *sock)
return err;
}
static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
{
BT_DBG("conn %p", conn);
sco_pi(sk)->conn = conn;
conn->sk = sk;
if (parent)
bt_accept_enqueue(parent, sk);
}
/* Delete channel.
* Must be called on the locked socket. */
static void sco_chan_del(struct sock *sk, int err)
{
struct sco_conn *conn;
conn = sco_pi(sk)->conn;
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_drop(conn->hcon);
}
sk->sk_state = BT_CLOSED;
sk->sk_err = err;
sk->sk_state_change(sk);
sock_set_flag(sk, SOCK_ZAPPED);
}
static void sco_conn_ready(struct sco_conn *conn)
{
struct sock *parent;

View file

@ -35,11 +35,13 @@
#define AUTH_REQ_MASK 0x07
#define SMP_FLAG_TK_VALID 1
#define SMP_FLAG_CFM_PENDING 2
#define SMP_FLAG_MITM_AUTH 3
#define SMP_FLAG_COMPLETE 4
#define SMP_FLAG_INITIATOR 5
enum {
SMP_FLAG_TK_VALID,
SMP_FLAG_CFM_PENDING,
SMP_FLAG_MITM_AUTH,
SMP_FLAG_COMPLETE,
SMP_FLAG_INITIATOR,
};
struct smp_chan {
struct l2cap_conn *conn;
@ -60,20 +62,16 @@ struct smp_chan {
struct smp_ltk *slave_ltk;
struct smp_irk *remote_irk;
unsigned long flags;
struct crypto_blkcipher *tfm_aes;
};
static inline void swap128(const u8 src[16], u8 dst[16])
static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
{
int i;
for (i = 0; i < 16; i++)
dst[15 - i] = src[i];
}
size_t i;
static inline void swap56(const u8 src[7], u8 dst[7])
{
int i;
for (i = 0; i < 7; i++)
dst[6 - i] = src[i];
for (i = 0; i < len; i++)
dst[len - 1 - i] = src[i];
}
static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
@ -92,7 +90,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
desc.flags = 0;
/* The most significant octet of key corresponds to k[0] */
swap128(k, tmp);
swap_buf(k, tmp, 16);
err = crypto_blkcipher_setkey(tfm, tmp, 16);
if (err) {
@ -101,7 +99,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
}
/* Most significant octet of plaintextData corresponds to data[0] */
swap128(r, data);
swap_buf(r, data, 16);
sg_init_one(&sg, data, 16);
@ -110,7 +108,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
BT_ERR("Encrypt data error %d", err);
/* Most significant octet of encryptedData corresponds to data[0] */
swap128(data, r);
swap_buf(data, r, 16);
return err;
}
@ -174,13 +172,16 @@ int smp_generate_rpa(struct crypto_blkcipher *tfm, u8 irk[16], bdaddr_t *rpa)
return 0;
}
static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
u8 _rat, bdaddr_t *ra, u8 res[16])
static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, bdaddr_t *ra,
u8 res[16])
{
struct hci_dev *hdev = smp->conn->hcon->hdev;
u8 p1[16], p2[16];
int err;
BT_DBG("%s", hdev->name);
memset(p1, 0, 16);
/* p1 = pres || preq || _rat || _iat */
@ -198,7 +199,7 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
/* res = e(k, res) */
err = smp_e(tfm, k, res);
err = smp_e(smp->tfm_aes, k, res);
if (err) {
BT_ERR("Encrypt data error");
return err;
@ -208,23 +209,26 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
/* res = e(k, res) */
err = smp_e(tfm, k, res);
err = smp_e(smp->tfm_aes, k, res);
if (err)
BT_ERR("Encrypt data error");
return err;
}
static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16],
u8 r2[16], u8 _r[16])
static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16],
u8 _r[16])
{
struct hci_dev *hdev = smp->conn->hcon->hdev;
int err;
BT_DBG("%s", hdev->name);
/* Just least significant octets from r1 and r2 are considered */
memcpy(_r, r2, 8);
memcpy(_r + 8, r1, 8);
err = smp_e(tfm, k, _r);
err = smp_e(smp->tfm_aes, k, _r);
if (err)
BT_ERR("Encrypt data error");
@ -303,7 +307,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
struct hci_dev *hdev = hcon->hdev;
u8 local_dist = 0, remote_dist = 0;
if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) {
if (test_bit(HCI_BONDABLE, &conn->hcon->hdev->dev_flags)) {
local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
authreq |= SMP_AUTH_BONDING;
@ -387,10 +391,12 @@ static const u8 gen_method[5][5] = {
static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io)
{
/* If either side has unknown io_caps, use JUST WORKS */
/* If either side has unknown io_caps, use JUST_CFM (which gets
* converted later to JUST_WORKS if we're initiators.
*/
if (local_io > SMP_IO_KEYBOARD_DISPLAY ||
remote_io > SMP_IO_KEYBOARD_DISPLAY)
return JUST_WORKS;
return JUST_CFM;
return gen_method[remote_io][local_io];
}
@ -410,21 +416,25 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io);
/* If neither side wants MITM, use JUST WORKS */
/* Otherwise, look up method from the table */
/* If neither side wants MITM, either "just" confirm an incoming
* request or use just-works for outgoing ones. The JUST_CFM
* will be converted to JUST_WORKS if necessary later in this
* function. If either side has MITM look up the method from the
* table.
*/
if (!(auth & SMP_AUTH_MITM))
method = JUST_WORKS;
method = JUST_CFM;
else
method = get_auth_method(smp, local_io, remote_io);
/* If not bonding, don't ask user to confirm a Zero TK */
if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM)
method = JUST_WORKS;
/* Don't confirm locally initiated pairing attempts */
if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
method = JUST_WORKS;
/* Don't bother user space with no IO capabilities */
if (method == JUST_CFM && hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
method = JUST_WORKS;
/* If Just Works, Continue with Zero TK */
if (method == JUST_WORKS) {
set_bit(SMP_FLAG_TK_VALID, &smp->flags);
@ -439,7 +449,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
* Confirms and the slave Enters the passkey.
*/
if (method == OVERLAP) {
if (hcon->link_mode & HCI_LM_MASTER)
if (hcon->role == HCI_ROLE_MASTER)
method = CFM_PASSKEY;
else
method = REQ_PASSKEY;
@ -477,23 +487,15 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
static u8 smp_confirm(struct smp_chan *smp)
{
struct l2cap_conn *conn = smp->conn;
struct hci_dev *hdev = conn->hcon->hdev;
struct crypto_blkcipher *tfm = hdev->tfm_aes;
struct smp_cmd_pairing_confirm cp;
int ret;
BT_DBG("conn %p", conn);
/* Prevent mutual access to hdev->tfm_aes */
hci_dev_lock(hdev);
ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
ret = smp_c1(smp, smp->tk, smp->prnd, smp->preq, smp->prsp,
conn->hcon->init_addr_type, &conn->hcon->init_addr,
conn->hcon->resp_addr_type, &conn->hcon->resp_addr,
cp.confirm_val);
hci_dev_unlock(hdev);
if (ret)
return SMP_UNSPECIFIED;
@ -508,25 +510,17 @@ static u8 smp_random(struct smp_chan *smp)
{
struct l2cap_conn *conn = smp->conn;
struct hci_conn *hcon = conn->hcon;
struct hci_dev *hdev = hcon->hdev;
struct crypto_blkcipher *tfm = hdev->tfm_aes;
u8 confirm[16];
int ret;
if (IS_ERR_OR_NULL(tfm))
if (IS_ERR_OR_NULL(smp->tfm_aes))
return SMP_UNSPECIFIED;
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
/* Prevent mutual access to hdev->tfm_aes */
hci_dev_lock(hdev);
ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
ret = smp_c1(smp, smp->tk, smp->rrnd, smp->preq, smp->prsp,
hcon->init_addr_type, &hcon->init_addr,
hcon->resp_addr_type, &hcon->resp_addr, confirm);
hci_dev_unlock(hdev);
if (ret)
return SMP_UNSPECIFIED;
@ -540,7 +534,7 @@ static u8 smp_random(struct smp_chan *smp)
__le64 rand = 0;
__le16 ediv = 0;
smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, stk);
smp_s1(smp, smp->tk, smp->rrnd, smp->prnd, stk);
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@ -550,6 +544,7 @@ static u8 smp_random(struct smp_chan *smp)
hci_le_start_enc(hcon, ediv, rand, stk);
hcon->enc_key_size = smp->enc_key_size;
set_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags);
} else {
u8 stk[16], auth;
__le64 rand = 0;
@ -558,7 +553,7 @@ static u8 smp_random(struct smp_chan *smp)
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
smp->prnd);
smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, stk);
smp_s1(smp, smp->tk, smp->prnd, smp->rrnd, stk);
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@ -568,9 +563,12 @@ static u8 smp_random(struct smp_chan *smp)
else
auth = 0;
/* Even though there's no _SLAVE suffix this is the
* slave STK we're adding for later lookup (the master
* STK never needs to be stored).
*/
hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
HCI_SMP_STK_SLAVE, auth, stk, smp->enc_key_size,
ediv, rand);
SMP_STK, auth, stk, smp->enc_key_size, ediv, rand);
}
return 0;
@ -581,12 +579,21 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
struct smp_chan *smp;
smp = kzalloc(sizeof(*smp), GFP_ATOMIC);
if (!smp)
if (!smp) {
clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags);
return NULL;
}
smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(smp->tfm_aes)) {
BT_ERR("Unable to create ECB crypto context");
kfree(smp);
clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags);
return NULL;
}
smp->conn = conn;
conn->smp_chan = smp;
conn->hcon->smp_conn = conn;
hci_conn_hold(conn->hcon);
@ -606,6 +613,8 @@ void smp_chan_destroy(struct l2cap_conn *conn)
kfree(smp->csrk);
kfree(smp->slave_csrk);
crypto_free_blkcipher(smp->tfm_aes);
/* If pairing failed clean up any keys we might have */
if (!complete) {
if (smp->ltk) {
@ -626,19 +635,18 @@ void smp_chan_destroy(struct l2cap_conn *conn)
kfree(smp);
conn->smp_chan = NULL;
conn->hcon->smp_conn = NULL;
hci_conn_drop(conn->hcon);
}
int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
{
struct l2cap_conn *conn = hcon->smp_conn;
struct l2cap_conn *conn = hcon->l2cap_data;
struct smp_chan *smp;
u32 value;
BT_DBG("");
if (!conn)
if (!conn || !test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
return -ENOTCONN;
smp = conn->smp_chan;
@ -675,6 +683,7 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_pairing rsp, *req = (void *) skb->data;
struct hci_dev *hdev = conn->hcon->hdev;
struct smp_chan *smp;
u8 key_size, auth, sec_level;
int ret;
@ -684,7 +693,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (skb->len < sizeof(*req))
return SMP_INVALID_PARAMS;
if (conn->hcon->link_mode & HCI_LM_MASTER)
if (conn->hcon->role != HCI_ROLE_SLAVE)
return SMP_CMD_NOTSUPP;
if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
@ -695,6 +704,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (!smp)
return SMP_UNSPECIFIED;
if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) &&
(req->auth_req & SMP_AUTH_BONDING))
return SMP_PAIRING_NOTSUPP;
smp->preq[0] = SMP_CMD_PAIRING_REQ;
memcpy(&smp->preq[1], req, sizeof(*req));
skb_pull(skb, sizeof(*req));
@ -734,8 +747,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (ret)
return SMP_UNSPECIFIED;
clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
return 0;
}
@ -751,7 +762,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
if (skb->len < sizeof(*rsp))
return SMP_INVALID_PARAMS;
if (!(conn->hcon->link_mode & HCI_LM_MASTER))
if (conn->hcon->role != HCI_ROLE_MASTER)
return SMP_CMD_NOTSUPP;
skb_pull(skb, sizeof(*rsp));
@ -839,26 +850,51 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
return smp_random(smp);
}
static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
{
struct smp_ltk *key;
struct hci_conn *hcon = conn->hcon;
key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
hcon->out);
hcon->role);
if (!key)
return 0;
return false;
if (sec_level > BT_SECURITY_MEDIUM && !key->authenticated)
return 0;
return false;
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
return 1;
return true;
hci_le_start_enc(hcon, key->ediv, key->rand, key->val);
hcon->enc_key_size = key->enc_size;
return 1;
/* We never store STKs for master role, so clear this flag */
clear_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags);
return true;
}
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
{
if (sec_level == BT_SECURITY_LOW)
return true;
/* If we're encrypted with an STK always claim insufficient
* security. This way we allow the connection to be re-encrypted
* with an LTK, even if the LTK provides the same level of
* security. Only exception is if we don't have an LTK (e.g.
* because of key distribution bits).
*/
if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
hcon->role))
return false;
if (hcon->sec_level >= sec_level)
return true;
return false;
}
static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
@ -874,10 +910,13 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (skb->len < sizeof(*rp))
return SMP_INVALID_PARAMS;
if (!(conn->hcon->link_mode & HCI_LM_MASTER))
if (hcon->role != HCI_ROLE_MASTER)
return SMP_CMD_NOTSUPP;
sec_level = authreq_to_seclevel(rp->auth_req);
if (smp_sufficient_security(hcon, sec_level))
return 0;
if (sec_level > hcon->pending_sec_level)
hcon->pending_sec_level = sec_level;
@ -888,6 +927,12 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
return 0;
smp = smp_chan_create(conn);
if (!smp)
return SMP_UNSPECIFIED;
if (!test_bit(HCI_BONDABLE, &hcon->hdev->dev_flags) &&
(rp->auth_req & SMP_AUTH_BONDING))
return SMP_PAIRING_NOTSUPP;
skb_pull(skb, sizeof(*rp));
@ -899,22 +944,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
return 0;
}
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
{
if (sec_level == BT_SECURITY_LOW)
return true;
if (hcon->sec_level >= sec_level)
return true;
return false;
}
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
{
struct l2cap_conn *conn = hcon->l2cap_data;
@ -936,7 +968,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
if (sec_level > hcon->pending_sec_level)
hcon->pending_sec_level = sec_level;
if (hcon->link_mode & HCI_LM_MASTER)
if (hcon->role == HCI_ROLE_MASTER)
if (smp_ltk_encrypt(conn, hcon->pending_sec_level))
return 0;
@ -956,7 +988,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
hcon->pending_sec_level > BT_SECURITY_MEDIUM)
authreq |= SMP_AUTH_MITM;
if (hcon->link_mode & HCI_LM_MASTER) {
if (hcon->role == HCI_ROLE_MASTER) {
struct smp_cmd_pairing cp;
build_pairing_cmd(conn, &cp, NULL, authreq);
@ -1021,7 +1053,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
hci_dev_lock(hdev);
authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK,
ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK,
authenticated, smp->tk, smp->enc_key_size,
rp->ediv, rp->rand);
smp->ltk = ltk;
@ -1075,6 +1107,8 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
skb_pull(skb, sizeof(*info));
hci_dev_lock(hcon->hdev);
/* Strictly speaking the Core Specification (4.1) allows sending
* an empty address which would force us to rely on just the IRK
* as "identity information". However, since such
@ -1084,8 +1118,7 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
*/
if (!bacmp(&info->bdaddr, BDADDR_ANY)) {
BT_ERR("Ignoring IRK with no identity address");
smp_distribute_keys(conn);
return 0;
goto distribute;
}
bacpy(&smp->id_addr, &info->bdaddr);
@ -1099,8 +1132,11 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
smp->remote_irk = hci_add_irk(conn->hcon->hdev, &smp->id_addr,
smp->id_addr_type, smp->irk, &rpa);
distribute:
smp_distribute_keys(conn);
hci_dev_unlock(hcon->hdev);
return 0;
}
@ -1156,7 +1192,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
}
if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) {
err = -ENOTSUPP;
err = -EOPNOTSUPP;
reason = SMP_PAIRING_NOTSUPP;
goto done;
}
@ -1174,7 +1210,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
!conn->smp_chan) {
BT_ERR("Unexpected SMP command 0x%02x. Disconnecting.", code);
kfree_skb(skb);
return -ENOTSUPP;
return -EOPNOTSUPP;
}
switch (code) {
@ -1258,6 +1294,22 @@ static void smp_notify_keys(struct l2cap_conn *conn)
bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
hcon->dst_type = smp->remote_irk->addr_type;
l2cap_conn_update_id_addr(hcon);
/* When receiving an indentity resolving key for
* a remote device that does not use a resolvable
* private address, just remove the key so that
* it is possible to use the controller white
* list for scanning.
*
* Userspace will have been told to not store
* this key at this point. So it is safe to
* just remove it.
*/
if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
list_del(&smp->remote_irk->list);
kfree(smp->remote_irk);
smp->remote_irk = NULL;
}
}
/* The LTKs and CSRKs should be persistent only if both sides
@ -1337,7 +1389,7 @@ int smp_distribute_keys(struct l2cap_conn *conn)
authenticated = hcon->sec_level == BT_SECURITY_HIGH;
ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type,
HCI_SMP_LTK_SLAVE, authenticated, enc.ltk,
SMP_LTK_SLAVE, authenticated, enc.ltk,
smp->enc_key_size, ediv, rand);
smp->slave_ltk = ltk;

View file

@ -116,6 +116,13 @@ struct smp_cmd_security_req {
#define SMP_MIN_ENC_KEY_SIZE 7
#define SMP_MAX_ENC_KEY_SIZE 16
/* LTK types used in internal storage (struct smp_ltk) */
enum {
SMP_STK,
SMP_LTK,
SMP_LTK_SLAVE,
};
/* SMP Commands */
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);

View file

@ -93,7 +93,7 @@ static void fdb_rcu_free(struct rcu_head *head)
static void fdb_add_hw(struct net_bridge *br, const unsigned char *addr)
{
int err;
struct net_bridge_port *p, *tmp;
struct net_bridge_port *p;
ASSERT_RTNL();
@ -107,11 +107,9 @@ static void fdb_add_hw(struct net_bridge *br, const unsigned char *addr)
return;
undo:
list_for_each_entry(tmp, &br->port_list, list) {
if (tmp == p)
break;
if (!br_promisc_port(tmp))
dev_uc_del(tmp->dev, addr);
list_for_each_entry_continue_reverse(p, &br->port_list, list) {
if (!br_promisc_port(p))
dev_uc_del(p->dev, addr);
}
}
@ -631,7 +629,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci))
goto nla_put_failure;
if (nla_put(skb, NDA_VLAN, sizeof(u16), &fdb->vlan_id))
if (fdb->vlan_id && nla_put(skb, NDA_VLAN, sizeof(u16), &fdb->vlan_id))
goto nla_put_failure;
return nlmsg_end(skb, nlh);
@ -678,6 +676,7 @@ errout:
int br_fdb_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev,
int idx)
{
struct net_bridge *br = netdev_priv(dev);
@ -693,6 +692,19 @@ int br_fdb_dump(struct sk_buff *skb,
if (idx < cb->args[0])
goto skip;
if (filter_dev &&
(!f->dst || f->dst->dev != filter_dev)) {
if (filter_dev != dev)
goto skip;
/* !f->dst is a speacial case for bridge
* It means the MAC belongs to the bridge
* Therefore need a little more filtering
* we only want to dump the !f->dst case
*/
if (f->dst)
goto skip;
}
if (fdb_fill_info(skb, br, f,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,

View file

@ -344,7 +344,7 @@ int br_add_bridge(struct net *net, const char *name)
struct net_device *dev;
int res;
dev = alloc_netdev(sizeof(struct net_bridge), name,
dev = alloc_netdev(sizeof(struct net_bridge), name, NET_NAME_UNKNOWN,
br_dev_setup);
if (!dev)

View file

@ -2215,6 +2215,43 @@ unlock:
}
EXPORT_SYMBOL_GPL(br_multicast_list_adjacent);
/**
* br_multicast_has_querier_anywhere - Checks for a querier on a bridge
* @dev: The bridge port providing the bridge on which to check for a querier
* @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6
*
* Checks whether the given interface has a bridge on top and if so returns
* true if a valid querier exists anywhere on the bridged link layer.
* Otherwise returns false.
*/
bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto)
{
struct net_bridge *br;
struct net_bridge_port *port;
struct ethhdr eth;
bool ret = false;
rcu_read_lock();
if (!br_port_exists(dev))
goto unlock;
port = br_port_get_rcu(dev);
if (!port || !port->br)
goto unlock;
br = port->br;
memset(&eth, 0, sizeof(eth));
eth.h_proto = htons(proto);
ret = br_multicast_querier_exists(br, &eth);
unlock:
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(br_multicast_has_querier_anywhere);
/**
* br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port
* @dev: The bridge port adjacent to which to check for a querier

View file

@ -208,7 +208,6 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
int err = 0;
struct net_bridge_port *port = br_port_get_rtnl(dev);
/* not a bridge port and */
if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN))
goto out;

View file

@ -399,7 +399,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
const unsigned char *addr, u16 nlh_flags);
int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev, int idx);
struct net_device *dev, struct net_device *fdev, int idx);
int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);

View file

@ -55,10 +55,8 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
if (p) {
/* Add VLAN to the device filter if it is supported.
* Stricly speaking, this is not necessary now, since
* devices are made promiscuous by the bridge, but if
* that ever changes this code will allow tagged
* traffic to enter the bridge.
* This ensures tagged traffic enters the bridge when
* promiscuous mode is disabled by br_manage_promisc().
*/
err = vlan_vid_add(dev, br->vlan_proto, vid);
if (err)

View file

@ -14,6 +14,15 @@ config NFT_BRIDGE_META
help
Add support for bridge dedicated meta key.
config NFT_BRIDGE_REJECT
tristate "Netfilter nf_tables bridge reject support"
depends on NFT_REJECT && NFT_REJECT_IPV4 && NFT_REJECT_IPV6
help
Add support to reject packets.
config NF_LOG_BRIDGE
tristate "Bridge packet logging"
endif # NF_TABLES_BRIDGE
menuconfig BRIDGE_NF_EBTABLES
@ -202,22 +211,6 @@ config BRIDGE_EBT_LOG
To compile it as a module, choose M here. If unsure, say N.
config BRIDGE_EBT_ULOG
tristate "ebt: ulog support (OBSOLETE)"
help
This option enables the old bridge-specific "ebt_ulog" implementation
which has been obsoleted by the new "nfnetlink_log" code (see
CONFIG_NETFILTER_NETLINK_LOG).
This option adds the ulog watcher, that you can use in any rule
in any ebtables table. The packet is passed to a userspace
logging daemon using netlink multicast sockets. This differs
from the log watcher in the sense that the complete packet is
sent to userspace instead of a descriptive text and that
netlink multicast sockets are used instead of the syslog.
To compile it as a module, choose M here. If unsure, say N.
config BRIDGE_EBT_NFLOG
tristate "ebt: nflog support"
help

View file

@ -4,6 +4,10 @@
obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o
obj-$(CONFIG_NFT_BRIDGE_META) += nft_meta_bridge.o
obj-$(CONFIG_NFT_BRIDGE_REJECT) += nft_reject_bridge.o
# packet logging
obj-$(CONFIG_NF_LOG_BRIDGE) += nf_log_bridge.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
@ -33,5 +37,4 @@ obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
# watchers
obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
obj-$(CONFIG_BRIDGE_EBT_ULOG) += ebt_ulog.o
obj-$(CONFIG_BRIDGE_EBT_NFLOG) += ebt_nflog.o

View file

@ -186,6 +186,10 @@ ebt_log_tg(struct sk_buff *skb, const struct xt_action_param *par)
li.u.log.level = info->loglevel;
li.u.log.logflags = info->bitmask;
/* Remember that we have to use ebt_log_packet() not to break backward
* compatibility. We cannot use the default bridge packet logger via
* nf_log_packet() with NFT_LOG_TYPE_LOG here. --Pablo
*/
if (info->bitmask & EBT_LOG_NFLOG)
nf_log_packet(net, NFPROTO_BRIDGE, par->hooknum, skb,
par->in, par->out, &li, "%s", info->prefix);
@ -205,54 +209,13 @@ static struct xt_target ebt_log_tg_reg __read_mostly = {
.me = THIS_MODULE,
};
static struct nf_logger ebt_log_logger __read_mostly = {
.name = "ebt_log",
.logfn = &ebt_log_packet,
.me = THIS_MODULE,
};
static int __net_init ebt_log_net_init(struct net *net)
{
nf_log_set(net, NFPROTO_BRIDGE, &ebt_log_logger);
return 0;
}
static void __net_exit ebt_log_net_fini(struct net *net)
{
nf_log_unset(net, &ebt_log_logger);
}
static struct pernet_operations ebt_log_net_ops = {
.init = ebt_log_net_init,
.exit = ebt_log_net_fini,
};
static int __init ebt_log_init(void)
{
int ret;
ret = register_pernet_subsys(&ebt_log_net_ops);
if (ret < 0)
goto err_pernet;
ret = xt_register_target(&ebt_log_tg_reg);
if (ret < 0)
goto err_target;
nf_log_register(NFPROTO_BRIDGE, &ebt_log_logger);
return ret;
err_target:
unregister_pernet_subsys(&ebt_log_net_ops);
err_pernet:
return ret;
return xt_register_target(&ebt_log_tg_reg);
}
static void __exit ebt_log_fini(void)
{
unregister_pernet_subsys(&ebt_log_net_ops);
nf_log_unregister(&ebt_log_logger);
xt_unregister_target(&ebt_log_tg_reg);
}

View file

@ -1,393 +0,0 @@
/*
* netfilter module for userspace bridged Ethernet frames logging daemons
*
* Authors:
* Bart De Schuymer <bdschuym@pandora.be>
* Harald Welte <laforge@netfilter.org>
*
* November, 2004
*
* Based on ipt_ULOG.c, which is
* (C) 2000-2002 by Harald Welte <laforge@netfilter.org>
*
* This module accepts two parameters:
*
* nlbufsiz:
* The parameter specifies how big the buffer for each netlink multicast
* group is. e.g. If you say nlbufsiz=8192, up to eight kb of packets will
* get accumulated in the kernel until they are sent to userspace. It is
* NOT possible to allocate more than 128kB, and it is strongly discouraged,
* because atomically allocating 128kB inside the network rx softirq is not
* reliable. Please also keep in mind that this buffer size is allocated for
* each nlgroup you are using, so the total kernel memory usage increases
* by that factor.
*
* flushtimeout:
* Specify, after how many hundredths of a second the queue should be
* flushed even if it is not full yet.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <net/netlink.h>
#include <linux/netdevice.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_bridge/ebt_ulog.h>
#include <net/netfilter/nf_log.h>
#include <net/netns/generic.h>
#include <net/sock.h>
#include "../br_private.h"
static unsigned int nlbufsiz = NLMSG_GOODSIZE;
module_param(nlbufsiz, uint, 0600);
MODULE_PARM_DESC(nlbufsiz, "netlink buffer size (number of bytes) "
"(defaults to 4096)");
static unsigned int flushtimeout = 10;
module_param(flushtimeout, uint, 0600);
MODULE_PARM_DESC(flushtimeout, "buffer flush timeout (hundredths ofa second) "
"(defaults to 10)");
typedef struct {
unsigned int qlen; /* number of nlmsgs' in the skb */
struct nlmsghdr *lastnlh; /* netlink header of last msg in skb */
struct sk_buff *skb; /* the pre-allocated skb */
struct timer_list timer; /* the timer function */
spinlock_t lock; /* the per-queue lock */
} ebt_ulog_buff_t;
static int ebt_ulog_net_id __read_mostly;
struct ebt_ulog_net {
unsigned int nlgroup[EBT_ULOG_MAXNLGROUPS];
ebt_ulog_buff_t ulog_buffers[EBT_ULOG_MAXNLGROUPS];
struct sock *ebtulognl;
};
static struct ebt_ulog_net *ebt_ulog_pernet(struct net *net)
{
return net_generic(net, ebt_ulog_net_id);
}
/* send one ulog_buff_t to userspace */
static void ulog_send(struct ebt_ulog_net *ebt, unsigned int nlgroup)
{
ebt_ulog_buff_t *ub = &ebt->ulog_buffers[nlgroup];
del_timer(&ub->timer);
if (!ub->skb)
return;
/* last nlmsg needs NLMSG_DONE */
if (ub->qlen > 1)
ub->lastnlh->nlmsg_type = NLMSG_DONE;
NETLINK_CB(ub->skb).dst_group = nlgroup + 1;
netlink_broadcast(ebt->ebtulognl, ub->skb, 0, nlgroup + 1, GFP_ATOMIC);
ub->qlen = 0;
ub->skb = NULL;
}
/* timer function to flush queue in flushtimeout time */
static void ulog_timer(unsigned long data)
{
struct ebt_ulog_net *ebt = container_of((void *)data,
struct ebt_ulog_net,
nlgroup[*(unsigned int *)data]);
ebt_ulog_buff_t *ub = &ebt->ulog_buffers[*(unsigned int *)data];
spin_lock_bh(&ub->lock);
if (ub->skb)
ulog_send(ebt, *(unsigned int *)data);
spin_unlock_bh(&ub->lock);
}
static struct sk_buff *ulog_alloc_skb(unsigned int size)
{
struct sk_buff *skb;
unsigned int n;
n = max(size, nlbufsiz);
skb = alloc_skb(n, GFP_ATOMIC | __GFP_NOWARN);
if (!skb) {
if (n > size) {
/* try to allocate only as much as we need for
* current packet */
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb)
pr_debug("cannot even allocate buffer of size %ub\n",
size);
}
}
return skb;
}
static void ebt_ulog_packet(struct net *net, unsigned int hooknr,
const struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
const struct ebt_ulog_info *uloginfo,
const char *prefix)
{
ebt_ulog_packet_msg_t *pm;
size_t size, copy_len;
struct nlmsghdr *nlh;
struct ebt_ulog_net *ebt = ebt_ulog_pernet(net);
unsigned int group = uloginfo->nlgroup;
ebt_ulog_buff_t *ub = &ebt->ulog_buffers[group];
spinlock_t *lock = &ub->lock;
ktime_t kt;
if ((uloginfo->cprange == 0) ||
(uloginfo->cprange > skb->len + ETH_HLEN))
copy_len = skb->len + ETH_HLEN;
else
copy_len = uloginfo->cprange;
size = nlmsg_total_size(sizeof(*pm) + copy_len);
if (size > nlbufsiz) {
pr_debug("Size %Zd needed, but nlbufsiz=%d\n", size, nlbufsiz);
return;
}
spin_lock_bh(lock);
if (!ub->skb) {
if (!(ub->skb = ulog_alloc_skb(size)))
goto unlock;
} else if (size > skb_tailroom(ub->skb)) {
ulog_send(ebt, group);
if (!(ub->skb = ulog_alloc_skb(size)))
goto unlock;
}
nlh = nlmsg_put(ub->skb, 0, ub->qlen, 0,
size - NLMSG_ALIGN(sizeof(*nlh)), 0);
if (!nlh) {
kfree_skb(ub->skb);
ub->skb = NULL;
goto unlock;
}
ub->qlen++;
pm = nlmsg_data(nlh);
memset(pm, 0, sizeof(*pm));
/* Fill in the ulog data */
pm->version = EBT_ULOG_VERSION;
kt = ktime_get_real();
pm->stamp = ktime_to_timeval(kt);
if (ub->qlen == 1)
ub->skb->tstamp = kt;
pm->data_len = copy_len;
pm->mark = skb->mark;
pm->hook = hooknr;
if (uloginfo->prefix != NULL)
strcpy(pm->prefix, uloginfo->prefix);
if (in) {
strcpy(pm->physindev, in->name);
/* If in isn't a bridge, then physindev==indev */
if (br_port_exists(in))
/* rcu_read_lock()ed by nf_hook_slow */
strcpy(pm->indev, br_port_get_rcu(in)->br->dev->name);
else
strcpy(pm->indev, in->name);
}
if (out) {
/* If out exists, then out is a bridge port */
strcpy(pm->physoutdev, out->name);
/* rcu_read_lock()ed by nf_hook_slow */
strcpy(pm->outdev, br_port_get_rcu(out)->br->dev->name);
}
if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0)
BUG();
if (ub->qlen > 1)
ub->lastnlh->nlmsg_flags |= NLM_F_MULTI;
ub->lastnlh = nlh;
if (ub->qlen >= uloginfo->qthreshold)
ulog_send(ebt, group);
else if (!timer_pending(&ub->timer)) {
ub->timer.expires = jiffies + flushtimeout * HZ / 100;
add_timer(&ub->timer);
}
unlock:
spin_unlock_bh(lock);
}
/* this function is registered with the netfilter core */
static void ebt_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum,
const struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, const struct nf_loginfo *li,
const char *prefix)
{
struct ebt_ulog_info loginfo;
if (!li || li->type != NF_LOG_TYPE_ULOG) {
loginfo.nlgroup = EBT_ULOG_DEFAULT_NLGROUP;
loginfo.cprange = 0;
loginfo.qthreshold = EBT_ULOG_DEFAULT_QTHRESHOLD;
loginfo.prefix[0] = '\0';
} else {
loginfo.nlgroup = li->u.ulog.group;
loginfo.cprange = li->u.ulog.copy_len;
loginfo.qthreshold = li->u.ulog.qthreshold;
strlcpy(loginfo.prefix, prefix, sizeof(loginfo.prefix));
}
ebt_ulog_packet(net, hooknum, skb, in, out, &loginfo, prefix);
}
static unsigned int
ebt_ulog_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
struct net *net = dev_net(par->in ? par->in : par->out);
ebt_ulog_packet(net, par->hooknum, skb, par->in, par->out,
par->targinfo, NULL);
return EBT_CONTINUE;
}
static int ebt_ulog_tg_check(const struct xt_tgchk_param *par)
{
struct ebt_ulog_info *uloginfo = par->targinfo;
if (!par->net->xt.ebt_ulog_warn_deprecated) {
pr_info("ebt_ulog is deprecated and it will be removed soon, "
"use ebt_nflog instead\n");
par->net->xt.ebt_ulog_warn_deprecated = true;
}
if (uloginfo->nlgroup > 31)
return -EINVAL;
uloginfo->prefix[EBT_ULOG_PREFIX_LEN - 1] = '\0';
if (uloginfo->qthreshold > EBT_ULOG_MAX_QLEN)
uloginfo->qthreshold = EBT_ULOG_MAX_QLEN;
return 0;
}
static struct xt_target ebt_ulog_tg_reg __read_mostly = {
.name = "ulog",
.revision = 0,
.family = NFPROTO_BRIDGE,
.target = ebt_ulog_tg,
.checkentry = ebt_ulog_tg_check,
.targetsize = sizeof(struct ebt_ulog_info),
.me = THIS_MODULE,
};
static struct nf_logger ebt_ulog_logger __read_mostly = {
.name = "ebt_ulog",
.logfn = &ebt_log_packet,
.me = THIS_MODULE,
};
static int __net_init ebt_ulog_net_init(struct net *net)
{
int i;
struct ebt_ulog_net *ebt = ebt_ulog_pernet(net);
struct netlink_kernel_cfg cfg = {
.groups = EBT_ULOG_MAXNLGROUPS,
};
/* initialize ulog_buffers */
for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
ebt->nlgroup[i] = i;
setup_timer(&ebt->ulog_buffers[i].timer, ulog_timer,
(unsigned long)&ebt->nlgroup[i]);
spin_lock_init(&ebt->ulog_buffers[i].lock);
}
ebt->ebtulognl = netlink_kernel_create(net, NETLINK_NFLOG, &cfg);
if (!ebt->ebtulognl)
return -ENOMEM;
nf_log_set(net, NFPROTO_BRIDGE, &ebt_ulog_logger);
return 0;
}
static void __net_exit ebt_ulog_net_fini(struct net *net)
{
int i;
struct ebt_ulog_net *ebt = ebt_ulog_pernet(net);
nf_log_unset(net, &ebt_ulog_logger);
for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
ebt_ulog_buff_t *ub = &ebt->ulog_buffers[i];
del_timer(&ub->timer);
if (ub->skb) {
kfree_skb(ub->skb);
ub->skb = NULL;
}
}
netlink_kernel_release(ebt->ebtulognl);
}
static struct pernet_operations ebt_ulog_net_ops = {
.init = ebt_ulog_net_init,
.exit = ebt_ulog_net_fini,
.id = &ebt_ulog_net_id,
.size = sizeof(struct ebt_ulog_net),
};
static int __init ebt_ulog_init(void)
{
int ret;
if (nlbufsiz >= 128*1024) {
pr_warn("Netlink buffer has to be <= 128kB,"
"please try a smaller nlbufsiz parameter.\n");
return -EINVAL;
}
ret = register_pernet_subsys(&ebt_ulog_net_ops);
if (ret)
goto out_pernet;
ret = xt_register_target(&ebt_ulog_tg_reg);
if (ret)
goto out_target;
nf_log_register(NFPROTO_BRIDGE, &ebt_ulog_logger);
return 0;
out_target:
unregister_pernet_subsys(&ebt_ulog_net_ops);
out_pernet:
return ret;
}
static void __exit ebt_ulog_fini(void)
{
nf_log_unregister(&ebt_ulog_logger);
xt_unregister_target(&ebt_ulog_tg_reg);
unregister_pernet_subsys(&ebt_ulog_net_ops);
}
module_init(ebt_ulog_init);
module_exit(ebt_ulog_fini);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bart De Schuymer <[email protected]>");
MODULE_DESCRIPTION("Ebtables: Packet logging to netlink using ULOG");

View file

@ -0,0 +1,96 @@
/*
* (C) 2014 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/skbuff.h>
#include <linux/if_bridge.h>
#include <linux/ip.h>
#include <net/route.h>
#include <linux/netfilter.h>
#include <net/netfilter/nf_log.h>
static void nf_log_bridge_packet(struct net *net, u_int8_t pf,
unsigned int hooknum,
const struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
const struct nf_loginfo *loginfo,
const char *prefix)
{
switch (eth_hdr(skb)->h_proto) {
case htons(ETH_P_IP):
nf_log_packet(net, NFPROTO_IPV4, hooknum, skb, in, out,
loginfo, "%s", prefix);
break;
case htons(ETH_P_IPV6):
nf_log_packet(net, NFPROTO_IPV6, hooknum, skb, in, out,
loginfo, "%s", prefix);
break;
case htons(ETH_P_ARP):
case htons(ETH_P_RARP):
nf_log_packet(net, NFPROTO_ARP, hooknum, skb, in, out,
loginfo, "%s", prefix);
break;
}
}
static struct nf_logger nf_bridge_logger __read_mostly = {
.name = "nf_log_bridge",
.type = NF_LOG_TYPE_LOG,
.logfn = nf_log_bridge_packet,
.me = THIS_MODULE,
};
static int __net_init nf_log_bridge_net_init(struct net *net)
{
nf_log_set(net, NFPROTO_BRIDGE, &nf_bridge_logger);
return 0;
}
static void __net_exit nf_log_bridge_net_exit(struct net *net)
{
nf_log_unset(net, &nf_bridge_logger);
}
static struct pernet_operations nf_log_bridge_net_ops = {
.init = nf_log_bridge_net_init,
.exit = nf_log_bridge_net_exit,
};
static int __init nf_log_bridge_init(void)
{
int ret;
/* Request to load the real packet loggers. */
nf_logger_request_module(NFPROTO_IPV4, NF_LOG_TYPE_LOG);
nf_logger_request_module(NFPROTO_IPV6, NF_LOG_TYPE_LOG);
nf_logger_request_module(NFPROTO_ARP, NF_LOG_TYPE_LOG);
ret = register_pernet_subsys(&nf_log_bridge_net_ops);
if (ret < 0)
return ret;
nf_log_register(NFPROTO_BRIDGE, &nf_bridge_logger);
return 0;
}
static void __exit nf_log_bridge_exit(void)
{
unregister_pernet_subsys(&nf_log_bridge_net_ops);
nf_log_unregister(&nf_bridge_logger);
}
module_init(nf_log_bridge_init);
module_exit(nf_log_bridge_exit);
MODULE_AUTHOR("Pablo Neira Ayuso <[email protected]>");
MODULE_DESCRIPTION("Netfilter bridge packet logging");
MODULE_LICENSE("GPL");
MODULE_ALIAS_NF_LOGGER(AF_BRIDGE, 0);

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_reject.h>
static void nft_reject_bridge_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
switch (eth_hdr(pkt->skb)->h_proto) {
case htons(ETH_P_IP):
return nft_reject_ipv4_eval(expr, data, pkt);
case htons(ETH_P_IPV6):
return nft_reject_ipv6_eval(expr, data, pkt);
default:
/* No explicit way to reject this protocol, drop it. */
data[NFT_REG_VERDICT].verdict = NF_DROP;
break;
}
}
static struct nft_expr_type nft_reject_bridge_type;
static const struct nft_expr_ops nft_reject_bridge_ops = {
.type = &nft_reject_bridge_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
.eval = nft_reject_bridge_eval,
.init = nft_reject_init,
.dump = nft_reject_dump,
};
static struct nft_expr_type nft_reject_bridge_type __read_mostly = {
.family = NFPROTO_BRIDGE,
.name = "reject",
.ops = &nft_reject_bridge_ops,
.policy = nft_reject_policy,
.maxattr = NFTA_REJECT_MAX,
.owner = THIS_MODULE,
};
static int __init nft_reject_bridge_module_init(void)
{
return nft_register_expr(&nft_reject_bridge_type);
}
static void __exit nft_reject_bridge_module_exit(void)
{
nft_unregister_expr(&nft_reject_bridge_type);
}
module_init(nft_reject_bridge_module_init);
module_exit(nft_reject_bridge_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pablo Neira Ayuso <[email protected]>");
MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "reject");

View file

@ -908,8 +908,7 @@ static int caif_release(struct socket *sock)
sock->sk = NULL;
WARN_ON(IS_ERR(cf_sk->debugfs_socket_dir));
if (cf_sk->debugfs_socket_dir != NULL)
debugfs_remove_recursive(cf_sk->debugfs_socket_dir);
debugfs_remove_recursive(cf_sk->debugfs_socket_dir);
lock_sock(&(cf_sk->sk));
sk->sk_state = CAIF_DISCONNECTED;

View file

@ -547,7 +547,6 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
default:
pr_err("Unrecognized Control Frame\n");
goto error;
break;
}
ret = 0;
error:

View file

@ -132,6 +132,7 @@
#include <linux/hashtable.h>
#include <linux/vmalloc.h>
#include <linux/if_macvlan.h>
#include <linux/errqueue.h>
#include "net-sysfs.h"
@ -1085,6 +1086,7 @@ static int dev_get_valid_name(struct net *net,
*/
int dev_change_name(struct net_device *dev, const char *newname)
{
unsigned char old_assign_type;
char oldname[IFNAMSIZ];
int err = 0;
int ret;
@ -1112,10 +1114,17 @@ int dev_change_name(struct net_device *dev, const char *newname)
return err;
}
if (oldname[0] && !strchr(oldname, '%'))
netdev_info(dev, "renamed from %s\n", oldname);
old_assign_type = dev->name_assign_type;
dev->name_assign_type = NET_NAME_RENAMED;
rollback:
ret = device_rename(&dev->dev, dev->name);
if (ret) {
memcpy(dev->name, oldname, IFNAMSIZ);
dev->name_assign_type = old_assign_type;
write_seqcount_end(&devnet_rename_seq);
return ret;
}
@ -1144,6 +1153,8 @@ rollback:
write_seqcount_begin(&devnet_rename_seq);
memcpy(dev->name, oldname, IFNAMSIZ);
memcpy(oldname, newname, IFNAMSIZ);
dev->name_assign_type = old_assign_type;
old_assign_type = NET_NAME_RENAMED;
goto rollback;
} else {
pr_err("%s: name change rollback failed: %d\n",
@ -2316,7 +2327,7 @@ __be16 skb_network_protocol(struct sk_buff *skb, int *depth)
*/
if (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
if (vlan_depth) {
if (unlikely(WARN_ON(vlan_depth < VLAN_HLEN)))
if (WARN_ON(vlan_depth < VLAN_HLEN))
return 0;
vlan_depth -= VLAN_HLEN;
} else {
@ -2414,8 +2425,8 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
skb_warn_bad_offload(skb);
if (skb_header_cloned(skb) &&
(err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
err = skb_cow_head(skb, 0);
if (err < 0)
return ERR_PTR(err);
}
@ -2745,8 +2756,8 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
/*
* Heuristic to force contended enqueues to serialize on a
* separate lock before trying to get qdisc main lock.
* This permits __QDISC_STATE_RUNNING owner to get the lock more often
* and dequeue packets faster.
* This permits __QDISC___STATE_RUNNING owner to get the lock more
* often and dequeue packets faster.
*/
contended = qdisc_is_running(q);
if (unlikely(contended))
@ -2866,6 +2877,9 @@ static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv)
skb_reset_mac_header(skb);
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_SCHED_TSTAMP))
__skb_tstamp_tx(skb, NULL, skb->sk, SCM_TSTAMP_SCHED);
/* Disable soft irqs for various locks below. Also
* stops preemption for RCU.
*/
@ -5440,13 +5454,9 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags)
*/
ret = 0;
if ((old_flags ^ flags) & IFF_UP) { /* Bit is different ? */
if ((old_flags ^ flags) & IFF_UP)
ret = ((old_flags & IFF_UP) ? __dev_close : __dev_open)(dev);
if (!ret)
dev_set_rx_mode(dev);
}
if ((flags ^ dev->gflags) & IFF_PROMISC) {
int inc = (flags & IFF_PROMISC) ? 1 : -1;
unsigned int old_flags = dev->flags;
@ -6446,17 +6456,19 @@ void netdev_freemem(struct net_device *dev)
/**
* alloc_netdev_mqs - allocate network device
* @sizeof_priv: size of private data to allocate space for
* @name: device name format string
* @setup: callback to initialize device
* @txqs: the number of TX subqueues to allocate
* @rxqs: the number of RX subqueues to allocate
* @sizeof_priv: size of private data to allocate space for
* @name: device name format string
* @name_assign_type: origin of device name
* @setup: callback to initialize device
* @txqs: the number of TX subqueues to allocate
* @rxqs: the number of RX subqueues to allocate
*
* Allocates a struct net_device with private data area for driver use
* and performs basic initialization. Also allocates subqueue structs
* for each queue on the device.
*/
struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
unsigned char name_assign_type,
void (*setup)(struct net_device *),
unsigned int txqs, unsigned int rxqs)
{
@ -6535,6 +6547,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
#endif
strcpy(dev->name, name);
dev->name_assign_type = name_assign_type;
dev->group = INIT_NETDEV_GROUP;
if (!dev->ethtool_ops)
dev->ethtool_ops = &default_ethtool_ops;
@ -6946,12 +6959,14 @@ static int __netdev_printk(const char *level, const struct net_device *dev,
if (dev && dev->dev.parent) {
r = dev_printk_emit(level[1] - '0',
dev->dev.parent,
"%s %s %s: %pV",
"%s %s %s%s: %pV",
dev_driver_string(dev->dev.parent),
dev_name(dev->dev.parent),
netdev_name(dev), vaf);
netdev_name(dev), netdev_reg_state(dev),
vaf);
} else if (dev) {
r = printk("%s%s: %pV", level, netdev_name(dev), vaf);
r = printk("%s%s%s: %pV", level, netdev_name(dev),
netdev_reg_state(dev), vaf);
} else {
r = printk("%s(NULL net_device): %pV", level, vaf);
}
@ -7103,7 +7118,7 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
rtnl_lock_unregistering(net_list);
list_for_each_entry(net, net_list, exit_list) {
for_each_netdev_reverse(net, dev) {
if (dev->rtnl_link_ops)
if (dev->rtnl_link_ops && dev->rtnl_link_ops->dellink)
dev->rtnl_link_ops->dellink(dev, &dev_kill_list);
else
unregister_netdevice_queue(dev, &dev_kill_list);

View file

@ -289,10 +289,8 @@ static int net_dm_cmd_trace(struct sk_buff *skb,
switch (info->genlhdr->cmd) {
case NET_DM_CMD_START:
return set_all_monitor_traces(TRACE_ON);
break;
case NET_DM_CMD_STOP:
return set_all_monitor_traces(TRACE_OFF);
break;
}
return -ENOTSUPP;

View file

@ -18,7 +18,7 @@
* 2 of the License, or (at your option) any later version.
*
* Andi Kleen - Fix a few bad bugs and races.
* Kris Katterjohn - Added many additional checks in sk_chk_filter()
* Kris Katterjohn - Added many additional checks in bpf_check_classic()
*/
#include <linux/module.h>
@ -45,54 +45,6 @@
#include <linux/seccomp.h>
#include <linux/if_vlan.h>
/* Registers */
#define BPF_R0 regs[BPF_REG_0]
#define BPF_R1 regs[BPF_REG_1]
#define BPF_R2 regs[BPF_REG_2]
#define BPF_R3 regs[BPF_REG_3]
#define BPF_R4 regs[BPF_REG_4]
#define BPF_R5 regs[BPF_REG_5]
#define BPF_R6 regs[BPF_REG_6]
#define BPF_R7 regs[BPF_REG_7]
#define BPF_R8 regs[BPF_REG_8]
#define BPF_R9 regs[BPF_REG_9]
#define BPF_R10 regs[BPF_REG_10]
/* Named registers */
#define DST regs[insn->dst_reg]
#define SRC regs[insn->src_reg]
#define FP regs[BPF_REG_FP]
#define ARG1 regs[BPF_REG_ARG1]
#define CTX regs[BPF_REG_CTX]
#define IMM insn->imm
/* No hurry in this branch
*
* Exported for the bpf jit load helper.
*/
void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, unsigned int size)
{
u8 *ptr = NULL;
if (k >= SKF_NET_OFF)
ptr = skb_network_header(skb) + k - SKF_NET_OFF;
else if (k >= SKF_LL_OFF)
ptr = skb_mac_header(skb) + k - SKF_LL_OFF;
if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb))
return ptr;
return NULL;
}
static inline void *load_pointer(const struct sk_buff *skb, int k,
unsigned int size, void *buffer)
{
if (k >= 0)
return skb_header_pointer(skb, k, size, buffer);
return bpf_internal_load_pointer_neg_helper(skb, k, size);
}
/**
* sk_filter - run a packet through a socket filter
* @sk: sock associated with &sk_buff
@ -135,451 +87,6 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)
}
EXPORT_SYMBOL(sk_filter);
/* Base function for offset calculation. Needs to go into .text section,
* therefore keeping it non-static as well; will also be used by JITs
* anyway later on, so do not let the compiler omit it.
*/
noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
{
return 0;
}
/**
* __sk_run_filter - run a filter on a given context
* @ctx: buffer to run the filter on
* @insn: filter to apply
*
* Decode and apply filter instructions to the skb->data. Return length to
* keep, 0 for none. @ctx is the data we are operating on, @insn is the
* array of filter instructions.
*/
static unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn)
{
u64 stack[MAX_BPF_STACK / sizeof(u64)];
u64 regs[MAX_BPF_REG], tmp;
static const void *jumptable[256] = {
[0 ... 255] = &&default_label,
/* Now overwrite non-defaults ... */
/* 32 bit ALU operations */
[BPF_ALU | BPF_ADD | BPF_X] = &&ALU_ADD_X,
[BPF_ALU | BPF_ADD | BPF_K] = &&ALU_ADD_K,
[BPF_ALU | BPF_SUB | BPF_X] = &&ALU_SUB_X,
[BPF_ALU | BPF_SUB | BPF_K] = &&ALU_SUB_K,
[BPF_ALU | BPF_AND | BPF_X] = &&ALU_AND_X,
[BPF_ALU | BPF_AND | BPF_K] = &&ALU_AND_K,
[BPF_ALU | BPF_OR | BPF_X] = &&ALU_OR_X,
[BPF_ALU | BPF_OR | BPF_K] = &&ALU_OR_K,
[BPF_ALU | BPF_LSH | BPF_X] = &&ALU_LSH_X,
[BPF_ALU | BPF_LSH | BPF_K] = &&ALU_LSH_K,
[BPF_ALU | BPF_RSH | BPF_X] = &&ALU_RSH_X,
[BPF_ALU | BPF_RSH | BPF_K] = &&ALU_RSH_K,
[BPF_ALU | BPF_XOR | BPF_X] = &&ALU_XOR_X,
[BPF_ALU | BPF_XOR | BPF_K] = &&ALU_XOR_K,
[BPF_ALU | BPF_MUL | BPF_X] = &&ALU_MUL_X,
[BPF_ALU | BPF_MUL | BPF_K] = &&ALU_MUL_K,
[BPF_ALU | BPF_MOV | BPF_X] = &&ALU_MOV_X,
[BPF_ALU | BPF_MOV | BPF_K] = &&ALU_MOV_K,
[BPF_ALU | BPF_DIV | BPF_X] = &&ALU_DIV_X,
[BPF_ALU | BPF_DIV | BPF_K] = &&ALU_DIV_K,
[BPF_ALU | BPF_MOD | BPF_X] = &&ALU_MOD_X,
[BPF_ALU | BPF_MOD | BPF_K] = &&ALU_MOD_K,
[BPF_ALU | BPF_NEG] = &&ALU_NEG,
[BPF_ALU | BPF_END | BPF_TO_BE] = &&ALU_END_TO_BE,
[BPF_ALU | BPF_END | BPF_TO_LE] = &&ALU_END_TO_LE,
/* 64 bit ALU operations */
[BPF_ALU64 | BPF_ADD | BPF_X] = &&ALU64_ADD_X,
[BPF_ALU64 | BPF_ADD | BPF_K] = &&ALU64_ADD_K,
[BPF_ALU64 | BPF_SUB | BPF_X] = &&ALU64_SUB_X,
[BPF_ALU64 | BPF_SUB | BPF_K] = &&ALU64_SUB_K,
[BPF_ALU64 | BPF_AND | BPF_X] = &&ALU64_AND_X,
[BPF_ALU64 | BPF_AND | BPF_K] = &&ALU64_AND_K,
[BPF_ALU64 | BPF_OR | BPF_X] = &&ALU64_OR_X,
[BPF_ALU64 | BPF_OR | BPF_K] = &&ALU64_OR_K,
[BPF_ALU64 | BPF_LSH | BPF_X] = &&ALU64_LSH_X,
[BPF_ALU64 | BPF_LSH | BPF_K] = &&ALU64_LSH_K,
[BPF_ALU64 | BPF_RSH | BPF_X] = &&ALU64_RSH_X,
[BPF_ALU64 | BPF_RSH | BPF_K] = &&ALU64_RSH_K,
[BPF_ALU64 | BPF_XOR | BPF_X] = &&ALU64_XOR_X,
[BPF_ALU64 | BPF_XOR | BPF_K] = &&ALU64_XOR_K,
[BPF_ALU64 | BPF_MUL | BPF_X] = &&ALU64_MUL_X,
[BPF_ALU64 | BPF_MUL | BPF_K] = &&ALU64_MUL_K,
[BPF_ALU64 | BPF_MOV | BPF_X] = &&ALU64_MOV_X,
[BPF_ALU64 | BPF_MOV | BPF_K] = &&ALU64_MOV_K,
[BPF_ALU64 | BPF_ARSH | BPF_X] = &&ALU64_ARSH_X,
[BPF_ALU64 | BPF_ARSH | BPF_K] = &&ALU64_ARSH_K,
[BPF_ALU64 | BPF_DIV | BPF_X] = &&ALU64_DIV_X,
[BPF_ALU64 | BPF_DIV | BPF_K] = &&ALU64_DIV_K,
[BPF_ALU64 | BPF_MOD | BPF_X] = &&ALU64_MOD_X,
[BPF_ALU64 | BPF_MOD | BPF_K] = &&ALU64_MOD_K,
[BPF_ALU64 | BPF_NEG] = &&ALU64_NEG,
/* Call instruction */
[BPF_JMP | BPF_CALL] = &&JMP_CALL,
/* Jumps */
[BPF_JMP | BPF_JA] = &&JMP_JA,
[BPF_JMP | BPF_JEQ | BPF_X] = &&JMP_JEQ_X,
[BPF_JMP | BPF_JEQ | BPF_K] = &&JMP_JEQ_K,
[BPF_JMP | BPF_JNE | BPF_X] = &&JMP_JNE_X,
[BPF_JMP | BPF_JNE | BPF_K] = &&JMP_JNE_K,
[BPF_JMP | BPF_JGT | BPF_X] = &&JMP_JGT_X,
[BPF_JMP | BPF_JGT | BPF_K] = &&JMP_JGT_K,
[BPF_JMP | BPF_JGE | BPF_X] = &&JMP_JGE_X,
[BPF_JMP | BPF_JGE | BPF_K] = &&JMP_JGE_K,
[BPF_JMP | BPF_JSGT | BPF_X] = &&JMP_JSGT_X,
[BPF_JMP | BPF_JSGT | BPF_K] = &&JMP_JSGT_K,
[BPF_JMP | BPF_JSGE | BPF_X] = &&JMP_JSGE_X,
[BPF_JMP | BPF_JSGE | BPF_K] = &&JMP_JSGE_K,
[BPF_JMP | BPF_JSET | BPF_X] = &&JMP_JSET_X,
[BPF_JMP | BPF_JSET | BPF_K] = &&JMP_JSET_K,
/* Program return */
[BPF_JMP | BPF_EXIT] = &&JMP_EXIT,
/* Store instructions */
[BPF_STX | BPF_MEM | BPF_B] = &&STX_MEM_B,
[BPF_STX | BPF_MEM | BPF_H] = &&STX_MEM_H,
[BPF_STX | BPF_MEM | BPF_W] = &&STX_MEM_W,
[BPF_STX | BPF_MEM | BPF_DW] = &&STX_MEM_DW,
[BPF_STX | BPF_XADD | BPF_W] = &&STX_XADD_W,
[BPF_STX | BPF_XADD | BPF_DW] = &&STX_XADD_DW,
[BPF_ST | BPF_MEM | BPF_B] = &&ST_MEM_B,
[BPF_ST | BPF_MEM | BPF_H] = &&ST_MEM_H,
[BPF_ST | BPF_MEM | BPF_W] = &&ST_MEM_W,
[BPF_ST | BPF_MEM | BPF_DW] = &&ST_MEM_DW,
/* Load instructions */
[BPF_LDX | BPF_MEM | BPF_B] = &&LDX_MEM_B,
[BPF_LDX | BPF_MEM | BPF_H] = &&LDX_MEM_H,
[BPF_LDX | BPF_MEM | BPF_W] = &&LDX_MEM_W,
[BPF_LDX | BPF_MEM | BPF_DW] = &&LDX_MEM_DW,
[BPF_LD | BPF_ABS | BPF_W] = &&LD_ABS_W,
[BPF_LD | BPF_ABS | BPF_H] = &&LD_ABS_H,
[BPF_LD | BPF_ABS | BPF_B] = &&LD_ABS_B,
[BPF_LD | BPF_IND | BPF_W] = &&LD_IND_W,
[BPF_LD | BPF_IND | BPF_H] = &&LD_IND_H,
[BPF_LD | BPF_IND | BPF_B] = &&LD_IND_B,
};
void *ptr;
int off;
#define CONT ({ insn++; goto select_insn; })
#define CONT_JMP ({ insn++; goto select_insn; })
FP = (u64) (unsigned long) &stack[ARRAY_SIZE(stack)];
ARG1 = (u64) (unsigned long) ctx;
/* Registers used in classic BPF programs need to be reset first. */
regs[BPF_REG_A] = 0;
regs[BPF_REG_X] = 0;
select_insn:
goto *jumptable[insn->code];
/* ALU */
#define ALU(OPCODE, OP) \
ALU64_##OPCODE##_X: \
DST = DST OP SRC; \
CONT; \
ALU_##OPCODE##_X: \
DST = (u32) DST OP (u32) SRC; \
CONT; \
ALU64_##OPCODE##_K: \
DST = DST OP IMM; \
CONT; \
ALU_##OPCODE##_K: \
DST = (u32) DST OP (u32) IMM; \
CONT;
ALU(ADD, +)
ALU(SUB, -)
ALU(AND, &)
ALU(OR, |)
ALU(LSH, <<)
ALU(RSH, >>)
ALU(XOR, ^)
ALU(MUL, *)
#undef ALU
ALU_NEG:
DST = (u32) -DST;
CONT;
ALU64_NEG:
DST = -DST;
CONT;
ALU_MOV_X:
DST = (u32) SRC;
CONT;
ALU_MOV_K:
DST = (u32) IMM;
CONT;
ALU64_MOV_X:
DST = SRC;
CONT;
ALU64_MOV_K:
DST = IMM;
CONT;
ALU64_ARSH_X:
(*(s64 *) &DST) >>= SRC;
CONT;
ALU64_ARSH_K:
(*(s64 *) &DST) >>= IMM;
CONT;
ALU64_MOD_X:
if (unlikely(SRC == 0))
return 0;
tmp = DST;
DST = do_div(tmp, SRC);
CONT;
ALU_MOD_X:
if (unlikely(SRC == 0))
return 0;
tmp = (u32) DST;
DST = do_div(tmp, (u32) SRC);
CONT;
ALU64_MOD_K:
tmp = DST;
DST = do_div(tmp, IMM);
CONT;
ALU_MOD_K:
tmp = (u32) DST;
DST = do_div(tmp, (u32) IMM);
CONT;
ALU64_DIV_X:
if (unlikely(SRC == 0))
return 0;
do_div(DST, SRC);
CONT;
ALU_DIV_X:
if (unlikely(SRC == 0))
return 0;
tmp = (u32) DST;
do_div(tmp, (u32) SRC);
DST = (u32) tmp;
CONT;
ALU64_DIV_K:
do_div(DST, IMM);
CONT;
ALU_DIV_K:
tmp = (u32) DST;
do_div(tmp, (u32) IMM);
DST = (u32) tmp;
CONT;
ALU_END_TO_BE:
switch (IMM) {
case 16:
DST = (__force u16) cpu_to_be16(DST);
break;
case 32:
DST = (__force u32) cpu_to_be32(DST);
break;
case 64:
DST = (__force u64) cpu_to_be64(DST);
break;
}
CONT;
ALU_END_TO_LE:
switch (IMM) {
case 16:
DST = (__force u16) cpu_to_le16(DST);
break;
case 32:
DST = (__force u32) cpu_to_le32(DST);
break;
case 64:
DST = (__force u64) cpu_to_le64(DST);
break;
}
CONT;
/* CALL */
JMP_CALL:
/* Function call scratches BPF_R1-BPF_R5 registers,
* preserves BPF_R6-BPF_R9, and stores return value
* into BPF_R0.
*/
BPF_R0 = (__bpf_call_base + insn->imm)(BPF_R1, BPF_R2, BPF_R3,
BPF_R4, BPF_R5);
CONT;
/* JMP */
JMP_JA:
insn += insn->off;
CONT;
JMP_JEQ_X:
if (DST == SRC) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JEQ_K:
if (DST == IMM) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JNE_X:
if (DST != SRC) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JNE_K:
if (DST != IMM) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JGT_X:
if (DST > SRC) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JGT_K:
if (DST > IMM) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JGE_X:
if (DST >= SRC) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JGE_K:
if (DST >= IMM) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JSGT_X:
if (((s64) DST) > ((s64) SRC)) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JSGT_K:
if (((s64) DST) > ((s64) IMM)) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JSGE_X:
if (((s64) DST) >= ((s64) SRC)) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JSGE_K:
if (((s64) DST) >= ((s64) IMM)) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JSET_X:
if (DST & SRC) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_JSET_K:
if (DST & IMM) {
insn += insn->off;
CONT_JMP;
}
CONT;
JMP_EXIT:
return BPF_R0;
/* STX and ST and LDX*/
#define LDST(SIZEOP, SIZE) \
STX_MEM_##SIZEOP: \
*(SIZE *)(unsigned long) (DST + insn->off) = SRC; \
CONT; \
ST_MEM_##SIZEOP: \
*(SIZE *)(unsigned long) (DST + insn->off) = IMM; \
CONT; \
LDX_MEM_##SIZEOP: \
DST = *(SIZE *)(unsigned long) (SRC + insn->off); \
CONT;
LDST(B, u8)
LDST(H, u16)
LDST(W, u32)
LDST(DW, u64)
#undef LDST
STX_XADD_W: /* lock xadd *(u32 *)(dst_reg + off16) += src_reg */
atomic_add((u32) SRC, (atomic_t *)(unsigned long)
(DST + insn->off));
CONT;
STX_XADD_DW: /* lock xadd *(u64 *)(dst_reg + off16) += src_reg */
atomic64_add((u64) SRC, (atomic64_t *)(unsigned long)
(DST + insn->off));
CONT;
LD_ABS_W: /* BPF_R0 = ntohl(*(u32 *) (skb->data + imm32)) */
off = IMM;
load_word:
/* BPF_LD + BPD_ABS and BPF_LD + BPF_IND insns are
* only appearing in the programs where ctx ==
* skb. All programs keep 'ctx' in regs[BPF_REG_CTX]
* == BPF_R6, sk_convert_filter() saves it in BPF_R6,
* internal BPF verifier will check that BPF_R6 ==
* ctx.
*
* BPF_ABS and BPF_IND are wrappers of function calls,
* so they scratch BPF_R1-BPF_R5 registers, preserve
* BPF_R6-BPF_R9, and store return value into BPF_R0.
*
* Implicit input:
* ctx == skb == BPF_R6 == CTX
*
* Explicit input:
* SRC == any register
* IMM == 32-bit immediate
*
* Output:
* BPF_R0 - 8/16/32-bit skb data converted to cpu endianness
*/
ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 4, &tmp);
if (likely(ptr != NULL)) {
BPF_R0 = get_unaligned_be32(ptr);
CONT;
}
return 0;
LD_ABS_H: /* BPF_R0 = ntohs(*(u16 *) (skb->data + imm32)) */
off = IMM;
load_half:
ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 2, &tmp);
if (likely(ptr != NULL)) {
BPF_R0 = get_unaligned_be16(ptr);
CONT;
}
return 0;
LD_ABS_B: /* BPF_R0 = *(u8 *) (skb->data + imm32) */
off = IMM;
load_byte:
ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 1, &tmp);
if (likely(ptr != NULL)) {
BPF_R0 = *(u8 *)ptr;
CONT;
}
return 0;
LD_IND_W: /* BPF_R0 = ntohl(*(u32 *) (skb->data + src_reg + imm32)) */
off = IMM + SRC;
goto load_word;
LD_IND_H: /* BPF_R0 = ntohs(*(u16 *) (skb->data + src_reg + imm32)) */
off = IMM + SRC;
goto load_half;
LD_IND_B: /* BPF_R0 = *(u8 *) (skb->data + src_reg + imm32) */
off = IMM + SRC;
goto load_byte;
default_label:
/* If we ever reach this, we have a bug somewhere. */
WARN_RATELIMIT(1, "unknown opcode %02x\n", insn->code);
return 0;
}
/* Helper to find the offset of pkt_type in sk_buff structure. We want
* to make sure its still a 3bit field starting at a byte boundary;
* taken from arch/x86/net/bpf_jit_comp.c.
@ -667,9 +174,9 @@ static u64 __get_random_u32(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
}
static bool convert_bpf_extensions(struct sock_filter *fp,
struct sock_filter_int **insnp)
struct bpf_insn **insnp)
{
struct sock_filter_int *insn = *insnp;
struct bpf_insn *insn = *insnp;
switch (fp->k) {
case SKF_AD_OFF + SKF_AD_PROTOCOL:
@ -805,7 +312,7 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
}
/**
* sk_convert_filter - convert filter program
* bpf_convert_filter - convert filter program
* @prog: the user passed filter program
* @len: the length of the user passed filter program
* @new_prog: buffer where converted program will be stored
@ -815,12 +322,12 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
* Conversion workflow:
*
* 1) First pass for calculating the new program length:
* sk_convert_filter(old_prog, old_len, NULL, &new_len)
* bpf_convert_filter(old_prog, old_len, NULL, &new_len)
*
* 2) 2nd pass to remap in two passes: 1st pass finds new
* jump offsets, 2nd pass remapping:
* new_prog = kmalloc(sizeof(struct sock_filter_int) * new_len);
* sk_convert_filter(old_prog, old_len, new_prog, &new_len);
* new_prog = kmalloc(sizeof(struct bpf_insn) * new_len);
* bpf_convert_filter(old_prog, old_len, new_prog, &new_len);
*
* User BPF's register A is mapped to our BPF register 6, user BPF
* register X is mapped to BPF register 7; frame pointer is always
@ -828,11 +335,11 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
* for socket filters: ctx == 'struct sk_buff *', for seccomp:
* ctx == 'struct seccomp_data *'.
*/
int sk_convert_filter(struct sock_filter *prog, int len,
struct sock_filter_int *new_prog, int *new_len)
int bpf_convert_filter(struct sock_filter *prog, int len,
struct bpf_insn *new_prog, int *new_len)
{
int new_flen = 0, pass = 0, target, i;
struct sock_filter_int *new_insn;
struct bpf_insn *new_insn;
struct sock_filter *fp;
int *addrs = NULL;
u8 bpf_src;
@ -858,8 +365,8 @@ do_pass:
new_insn++;
for (i = 0; i < len; fp++, i++) {
struct sock_filter_int tmp_insns[6] = { };
struct sock_filter_int *insn = tmp_insns;
struct bpf_insn tmp_insns[6] = { };
struct bpf_insn *insn = tmp_insns;
if (addrs)
addrs[i] = new_insn - new_prog;
@ -1094,7 +601,7 @@ err:
* a cell if not previously written, and we check all branches to be sure
* a malicious user doesn't try to abuse us.
*/
static int check_load_and_stores(struct sock_filter *filter, int flen)
static int check_load_and_stores(const struct sock_filter *filter, int flen)
{
u16 *masks, memvalid = 0; /* One bit per cell, 16 cells */
int pc, ret = 0;
@ -1214,7 +721,7 @@ static bool chk_code_allowed(u16 code_to_probe)
}
/**
* sk_chk_filter - verify socket filter code
* bpf_check_classic - verify socket filter code
* @filter: filter to verify
* @flen: length of filter
*
@ -1227,7 +734,7 @@ static bool chk_code_allowed(u16 code_to_probe)
*
* Returns 0 if the rule set is legal or -EINVAL if not.
*/
int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
int bpf_check_classic(const struct sock_filter *filter, unsigned int flen)
{
bool anc_found;
int pc;
@ -1237,7 +744,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
/* Check the filter code now */
for (pc = 0; pc < flen; pc++) {
struct sock_filter *ftest = &filter[pc];
const struct sock_filter *ftest = &filter[pc];
/* May we actually operate on this code? */
if (!chk_code_allowed(ftest->code))
@ -1301,12 +808,12 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
return -EINVAL;
}
EXPORT_SYMBOL(sk_chk_filter);
EXPORT_SYMBOL(bpf_check_classic);
static int sk_store_orig_filter(struct sk_filter *fp,
const struct sock_fprog *fprog)
static int bpf_prog_store_orig_filter(struct bpf_prog *fp,
const struct sock_fprog *fprog)
{
unsigned int fsize = sk_filter_proglen(fprog);
unsigned int fsize = bpf_classic_proglen(fprog);
struct sock_fprog_kern *fkprog;
fp->orig_prog = kmalloc(sizeof(*fkprog), GFP_KERNEL);
@ -1324,7 +831,7 @@ static int sk_store_orig_filter(struct sk_filter *fp,
return 0;
}
static void sk_release_orig_filter(struct sk_filter *fp)
static void bpf_release_orig_filter(struct bpf_prog *fp)
{
struct sock_fprog_kern *fprog = fp->orig_prog;
@ -1334,6 +841,18 @@ static void sk_release_orig_filter(struct sk_filter *fp)
}
}
static void __bpf_prog_release(struct bpf_prog *prog)
{
bpf_release_orig_filter(prog);
bpf_prog_free(prog);
}
static void __sk_filter_release(struct sk_filter *fp)
{
__bpf_prog_release(fp->prog);
kfree(fp);
}
/**
* sk_filter_release_rcu - Release a socket filter by rcu_head
* @rcu: rcu_head that contains the sk_filter to free
@ -1342,8 +861,7 @@ static void sk_filter_release_rcu(struct rcu_head *rcu)
{
struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu);
sk_release_orig_filter(fp);
sk_filter_free(fp);
__sk_filter_release(fp);
}
/**
@ -1360,44 +878,33 @@ static void sk_filter_release(struct sk_filter *fp)
void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp)
{
atomic_sub(sk_filter_size(fp->len), &sk->sk_omem_alloc);
u32 filter_size = bpf_prog_size(fp->prog->len);
atomic_sub(filter_size, &sk->sk_omem_alloc);
sk_filter_release(fp);
}
void sk_filter_charge(struct sock *sk, struct sk_filter *fp)
/* try to charge the socket memory if there is space available
* return true on success
*/
bool sk_filter_charge(struct sock *sk, struct sk_filter *fp)
{
atomic_inc(&fp->refcnt);
atomic_add(sk_filter_size(fp->len), &sk->sk_omem_alloc);
}
u32 filter_size = bpf_prog_size(fp->prog->len);
static struct sk_filter *__sk_migrate_realloc(struct sk_filter *fp,
struct sock *sk,
unsigned int len)
{
struct sk_filter *fp_new;
if (sk == NULL)
return krealloc(fp, len, GFP_KERNEL);
fp_new = sock_kmalloc(sk, len, GFP_KERNEL);
if (fp_new) {
*fp_new = *fp;
/* As we're keeping orig_prog in fp_new along,
* we need to make sure we're not evicting it
* from the old fp.
*/
fp->orig_prog = NULL;
sk_filter_uncharge(sk, fp);
/* same check as in sock_kmalloc() */
if (filter_size <= sysctl_optmem_max &&
atomic_read(&sk->sk_omem_alloc) + filter_size < sysctl_optmem_max) {
atomic_inc(&fp->refcnt);
atomic_add(filter_size, &sk->sk_omem_alloc);
return true;
}
return fp_new;
return false;
}
static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
struct sock *sk)
static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp)
{
struct sock_filter *old_prog;
struct sk_filter *old_fp;
struct bpf_prog *old_fp;
int err, new_len, old_len = fp->len;
/* We are free to overwrite insns et al right here as it
@ -1406,7 +913,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
* representation.
*/
BUILD_BUG_ON(sizeof(struct sock_filter) !=
sizeof(struct sock_filter_int));
sizeof(struct bpf_insn));
/* Conversion cannot happen on overlapping memory areas,
* so we need to keep the user BPF around until the 2nd
@ -1420,13 +927,13 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
}
/* 1st pass: calculate the new program length. */
err = sk_convert_filter(old_prog, old_len, NULL, &new_len);
err = bpf_convert_filter(old_prog, old_len, NULL, &new_len);
if (err)
goto out_err_free;
/* Expand fp for appending the new filter representation. */
old_fp = fp;
fp = __sk_migrate_realloc(old_fp, sk, sk_filter_size(new_len));
fp = krealloc(old_fp, bpf_prog_size(new_len), GFP_KERNEL);
if (!fp) {
/* The old_fp is still around in case we couldn't
* allocate new memory, so uncharge on that one.
@ -1438,17 +945,17 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
fp->len = new_len;
/* 2nd pass: remap sock_filter insns into sock_filter_int insns. */
err = sk_convert_filter(old_prog, old_len, fp->insnsi, &new_len);
/* 2nd pass: remap sock_filter insns into bpf_insn insns. */
err = bpf_convert_filter(old_prog, old_len, fp->insnsi, &new_len);
if (err)
/* 2nd sk_convert_filter() can fail only if it fails
/* 2nd bpf_convert_filter() can fail only if it fails
* to allocate memory, remapping must succeed. Note,
* that at this time old_fp has already been released
* by __sk_migrate_realloc().
* by krealloc().
*/
goto out_err_free;
sk_filter_select_runtime(fp);
bpf_prog_select_runtime(fp);
kfree(old_prog);
return fp;
@ -1456,55 +963,20 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
out_err_free:
kfree(old_prog);
out_err:
/* Rollback filter setup. */
if (sk != NULL)
sk_filter_uncharge(sk, fp);
else
kfree(fp);
__bpf_prog_release(fp);
return ERR_PTR(err);
}
void __weak bpf_int_jit_compile(struct sk_filter *prog)
{
}
/**
* sk_filter_select_runtime - select execution runtime for BPF program
* @fp: sk_filter populated with internal BPF program
*
* try to JIT internal BPF program, if JIT is not available select interpreter
* BPF program will be executed via SK_RUN_FILTER() macro
*/
void sk_filter_select_runtime(struct sk_filter *fp)
{
fp->bpf_func = (void *) __sk_run_filter;
/* Probe if internal BPF can be JITed */
bpf_int_jit_compile(fp);
}
EXPORT_SYMBOL_GPL(sk_filter_select_runtime);
/* free internal BPF program */
void sk_filter_free(struct sk_filter *fp)
{
bpf_jit_free(fp);
}
EXPORT_SYMBOL_GPL(sk_filter_free);
static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp,
struct sock *sk)
static struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp)
{
int err;
fp->bpf_func = NULL;
fp->jited = 0;
err = sk_chk_filter(fp->insns, fp->len);
err = bpf_check_classic(fp->insns, fp->len);
if (err) {
if (sk != NULL)
sk_filter_uncharge(sk, fp);
else
kfree(fp);
__bpf_prog_release(fp);
return ERR_PTR(err);
}
@ -1517,13 +989,13 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp,
* internal BPF translation for the optimized interpreter.
*/
if (!fp->jited)
fp = __sk_migrate_filter(fp, sk);
fp = bpf_migrate_filter(fp);
return fp;
}
/**
* sk_unattached_filter_create - create an unattached filter
* bpf_prog_create - create an unattached filter
* @pfp: the unattached filter that is created
* @fprog: the filter program
*
@ -1532,23 +1004,21 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp,
* If an error occurs or there is insufficient memory for the filter
* a negative errno code is returned. On success the return is zero.
*/
int sk_unattached_filter_create(struct sk_filter **pfp,
struct sock_fprog_kern *fprog)
int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog)
{
unsigned int fsize = sk_filter_proglen(fprog);
struct sk_filter *fp;
unsigned int fsize = bpf_classic_proglen(fprog);
struct bpf_prog *fp;
/* Make sure new filter is there and in the right amounts. */
if (fprog->filter == NULL)
return -EINVAL;
fp = kmalloc(sk_filter_size(fprog->len), GFP_KERNEL);
fp = kmalloc(bpf_prog_size(fprog->len), GFP_KERNEL);
if (!fp)
return -ENOMEM;
memcpy(fp->insns, fprog->filter, fsize);
atomic_set(&fp->refcnt, 1);
fp->len = fprog->len;
/* Since unattached filters are not copied back to user
* space through sk_get_filter(), we do not need to hold
@ -1556,23 +1026,23 @@ int sk_unattached_filter_create(struct sk_filter **pfp,
*/
fp->orig_prog = NULL;
/* __sk_prepare_filter() already takes care of uncharging
/* bpf_prepare_filter() already takes care of freeing
* memory in case something goes wrong.
*/
fp = __sk_prepare_filter(fp, NULL);
fp = bpf_prepare_filter(fp);
if (IS_ERR(fp))
return PTR_ERR(fp);
*pfp = fp;
return 0;
}
EXPORT_SYMBOL_GPL(sk_unattached_filter_create);
EXPORT_SYMBOL_GPL(bpf_prog_create);
void sk_unattached_filter_destroy(struct sk_filter *fp)
void bpf_prog_destroy(struct bpf_prog *fp)
{
sk_filter_release(fp);
__bpf_prog_release(fp);
}
EXPORT_SYMBOL_GPL(sk_unattached_filter_destroy);
EXPORT_SYMBOL_GPL(bpf_prog_destroy);
/**
* sk_attach_filter - attach a socket filter
@ -1587,8 +1057,9 @@ EXPORT_SYMBOL_GPL(sk_unattached_filter_destroy);
int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
{
struct sk_filter *fp, *old_fp;
unsigned int fsize = sk_filter_proglen(fprog);
unsigned int sk_fsize = sk_filter_size(fprog->len);
unsigned int fsize = bpf_classic_proglen(fprog);
unsigned int bpf_fsize = bpf_prog_size(fprog->len);
struct bpf_prog *prog;
int err;
if (sock_flag(sk, SOCK_FILTER_LOCKED))
@ -1598,30 +1069,43 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
if (fprog->filter == NULL)
return -EINVAL;
fp = sock_kmalloc(sk, sk_fsize, GFP_KERNEL);
if (!fp)
prog = kmalloc(bpf_fsize, GFP_KERNEL);
if (!prog)
return -ENOMEM;
if (copy_from_user(fp->insns, fprog->filter, fsize)) {
sock_kfree_s(sk, fp, sk_fsize);
if (copy_from_user(prog->insns, fprog->filter, fsize)) {
kfree(prog);
return -EFAULT;
}
atomic_set(&fp->refcnt, 1);
fp->len = fprog->len;
prog->len = fprog->len;
err = sk_store_orig_filter(fp, fprog);
err = bpf_prog_store_orig_filter(prog, fprog);
if (err) {
sk_filter_uncharge(sk, fp);
kfree(prog);
return -ENOMEM;
}
/* __sk_prepare_filter() already takes care of uncharging
/* bpf_prepare_filter() already takes care of freeing
* memory in case something goes wrong.
*/
fp = __sk_prepare_filter(fp, sk);
if (IS_ERR(fp))
return PTR_ERR(fp);
prog = bpf_prepare_filter(prog);
if (IS_ERR(prog))
return PTR_ERR(prog);
fp = kmalloc(sizeof(*fp), GFP_KERNEL);
if (!fp) {
__bpf_prog_release(prog);
return -ENOMEM;
}
fp->prog = prog;
atomic_set(&fp->refcnt, 0);
if (!sk_filter_charge(sk, fp)) {
__sk_filter_release(fp);
return -ENOMEM;
}
old_fp = rcu_dereference_protected(sk->sk_filter,
sock_owned_by_user(sk));
@ -1670,7 +1154,7 @@ int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
/* We're copying the filter that has been originally attached,
* so no conversion/decode needed anymore.
*/
fprog = filter->orig_prog;
fprog = filter->prog->orig_prog;
ret = fprog->len;
if (!len)
@ -1682,7 +1166,7 @@ int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
goto out;
ret = -EFAULT;
if (copy_to_user(ubuf, fprog->filter, sk_filter_proglen(fprog)))
if (copy_to_user(ubuf, fprog->filter, bpf_classic_proglen(fprog)))
goto out;
/* Instead of bytes, the API requests to return the number

View file

@ -80,6 +80,8 @@ ip:
case htons(ETH_P_IPV6): {
const struct ipv6hdr *iph;
struct ipv6hdr _iph;
__be32 flow_label;
ipv6:
iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
if (!iph)
@ -89,6 +91,21 @@ ipv6:
flow->src = (__force __be32)ipv6_addr_hash(&iph->saddr);
flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
nhoff += sizeof(struct ipv6hdr);
flow_label = ip6_flowlabel(iph);
if (flow_label) {
/* Awesome, IPv6 packet has a flow label so we can
* use that to represent the ports without any
* further dissection.
*/
flow->n_proto = proto;
flow->ip_proto = ip_proto;
flow->ports = flow_label;
flow->thoff = (u16)nhoff;
return true;
}
break;
}
case htons(ETH_P_8021AD):
@ -175,6 +192,7 @@ ipv6:
break;
}
flow->n_proto = proto;
flow->ip_proto = ip_proto;
flow->ports = skb_flow_get_ports(skb, nhoff, ip_proto);
flow->thoff = (u16) nhoff;
@ -195,12 +213,33 @@ static __always_inline u32 __flow_hash_3words(u32 a, u32 b, u32 c)
return jhash_3words(a, b, c, hashrnd);
}
static __always_inline u32 __flow_hash_1word(u32 a)
static inline u32 __flow_hash_from_keys(struct flow_keys *keys)
{
__flow_hash_secret_init();
return jhash_1word(a, hashrnd);
u32 hash;
/* get a consistent hash (same value on both flow directions) */
if (((__force u32)keys->dst < (__force u32)keys->src) ||
(((__force u32)keys->dst == (__force u32)keys->src) &&
((__force u16)keys->port16[1] < (__force u16)keys->port16[0]))) {
swap(keys->dst, keys->src);
swap(keys->port16[0], keys->port16[1]);
}
hash = __flow_hash_3words((__force u32)keys->dst,
(__force u32)keys->src,
(__force u32)keys->ports);
if (!hash)
hash = 1;
return hash;
}
u32 flow_hash_from_keys(struct flow_keys *keys)
{
return __flow_hash_from_keys(keys);
}
EXPORT_SYMBOL(flow_hash_from_keys);
/*
* __skb_get_hash: calculate a flow hash based on src/dst addresses
* and src/dst port numbers. Sets hash in skb to non-zero hash value
@ -210,7 +249,6 @@ static __always_inline u32 __flow_hash_1word(u32 a)
void __skb_get_hash(struct sk_buff *skb)
{
struct flow_keys keys;
u32 hash;
if (!skb_flow_dissect(skb, &keys))
return;
@ -218,21 +256,9 @@ void __skb_get_hash(struct sk_buff *skb)
if (keys.ports)
skb->l4_hash = 1;
/* get a consistent hash (same value on both flow directions) */
if (((__force u32)keys.dst < (__force u32)keys.src) ||
(((__force u32)keys.dst == (__force u32)keys.src) &&
((__force u16)keys.port16[1] < (__force u16)keys.port16[0]))) {
swap(keys.dst, keys.src);
swap(keys.port16[0], keys.port16[1]);
}
skb->sw_hash = 1;
hash = __flow_hash_3words((__force u32)keys.dst,
(__force u32)keys.src,
(__force u32)keys.ports);
if (!hash)
hash = 1;
skb->hash = hash;
skb->hash = __flow_hash_from_keys(&keys);
}
EXPORT_SYMBOL(__skb_get_hash);
@ -240,7 +266,7 @@ EXPORT_SYMBOL(__skb_get_hash);
* Returns a Tx hash based on the given packet descriptor a Tx queues' number
* to be used as a distribution range.
*/
u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb,
u16 __skb_tx_hash(const struct net_device *dev, struct sk_buff *skb,
unsigned int num_tx_queues)
{
u32 hash;
@ -260,13 +286,7 @@ u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb,
qcount = dev->tc_to_txq[tc].count;
}
if (skb->sk && skb->sk->sk_hash)
hash = skb->sk->sk_hash;
else
hash = (__force u16) skb->protocol;
hash = __flow_hash_1word(hash);
return (u16) (((u64) hash * qcount) >> 32) + qoffset;
return (u16) (((u64)skb_get_hash(skb) * qcount) >> 32) + qoffset;
}
EXPORT_SYMBOL(__skb_tx_hash);
@ -338,17 +358,10 @@ static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb)
if (map) {
if (map->len == 1)
queue_index = map->queues[0];
else {
u32 hash;
if (skb->sk && skb->sk->sk_hash)
hash = skb->sk->sk_hash;
else
hash = (__force u16) skb->protocol ^
skb->hash;
hash = __flow_hash_1word(hash);
else
queue_index = map->queues[
((u64)hash * map->len) >> 32];
}
((u64)skb_get_hash(skb) * map->len) >> 32];
if (unlikely(queue_index >= dev->real_num_tx_queues))
queue_index = -1;
}

View file

@ -43,12 +43,12 @@ static ssize_t netdev_show(const struct device *dev,
struct device_attribute *attr, char *buf,
ssize_t (*format)(const struct net_device *, char *))
{
struct net_device *net = to_net_dev(dev);
struct net_device *ndev = to_net_dev(dev);
ssize_t ret = -EINVAL;
read_lock(&dev_base_lock);
if (dev_isalive(net))
ret = (*format)(net, buf);
if (dev_isalive(ndev))
ret = (*format)(ndev, buf);
read_unlock(&dev_base_lock);
return ret;
@ -56,9 +56,9 @@ static ssize_t netdev_show(const struct device *dev,
/* generate a show function for simple field */
#define NETDEVICE_SHOW(field, format_string) \
static ssize_t format_##field(const struct net_device *net, char *buf) \
static ssize_t format_##field(const struct net_device *dev, char *buf) \
{ \
return sprintf(buf, format_string, net->field); \
return sprintf(buf, format_string, dev->field); \
} \
static ssize_t field##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
@ -112,16 +112,35 @@ NETDEVICE_SHOW_RO(ifindex, fmt_dec);
NETDEVICE_SHOW_RO(type, fmt_dec);
NETDEVICE_SHOW_RO(link_mode, fmt_dec);
static ssize_t format_name_assign_type(const struct net_device *dev, char *buf)
{
return sprintf(buf, fmt_dec, dev->name_assign_type);
}
static ssize_t name_assign_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct net_device *ndev = to_net_dev(dev);
ssize_t ret = -EINVAL;
if (ndev->name_assign_type != NET_NAME_UNKNOWN)
ret = netdev_show(dev, attr, buf, format_name_assign_type);
return ret;
}
static DEVICE_ATTR_RO(name_assign_type);
/* use same locking rules as GIFHWADDR ioctl's */
static ssize_t address_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct net_device *net = to_net_dev(dev);
struct net_device *ndev = to_net_dev(dev);
ssize_t ret = -EINVAL;
read_lock(&dev_base_lock);
if (dev_isalive(net))
ret = sysfs_format_mac(buf, net->dev_addr, net->addr_len);
if (dev_isalive(ndev))
ret = sysfs_format_mac(buf, ndev->dev_addr, ndev->addr_len);
read_unlock(&dev_base_lock);
return ret;
}
@ -130,18 +149,18 @@ static DEVICE_ATTR_RO(address);
static ssize_t broadcast_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct net_device *net = to_net_dev(dev);
if (dev_isalive(net))
return sysfs_format_mac(buf, net->broadcast, net->addr_len);
struct net_device *ndev = to_net_dev(dev);
if (dev_isalive(ndev))
return sysfs_format_mac(buf, ndev->broadcast, ndev->addr_len);
return -EINVAL;
}
static DEVICE_ATTR_RO(broadcast);
static int change_carrier(struct net_device *net, unsigned long new_carrier)
static int change_carrier(struct net_device *dev, unsigned long new_carrier)
{
if (!netif_running(net))
if (!netif_running(dev))
return -EINVAL;
return dev_change_carrier(net, (bool) new_carrier);
return dev_change_carrier(dev, (bool) new_carrier);
}
static ssize_t carrier_store(struct device *dev, struct device_attribute *attr,
@ -265,9 +284,9 @@ static DEVICE_ATTR_RO(carrier_changes);
/* read-write attributes */
static int change_mtu(struct net_device *net, unsigned long new_mtu)
static int change_mtu(struct net_device *dev, unsigned long new_mtu)
{
return dev_set_mtu(net, (int) new_mtu);
return dev_set_mtu(dev, (int) new_mtu);
}
static ssize_t mtu_store(struct device *dev, struct device_attribute *attr,
@ -277,9 +296,9 @@ static ssize_t mtu_store(struct device *dev, struct device_attribute *attr,
}
NETDEVICE_SHOW_RW(mtu, fmt_dec);
static int change_flags(struct net_device *net, unsigned long new_flags)
static int change_flags(struct net_device *dev, unsigned long new_flags)
{
return dev_change_flags(net, (unsigned int) new_flags);
return dev_change_flags(dev, (unsigned int) new_flags);
}
static ssize_t flags_store(struct device *dev, struct device_attribute *attr,
@ -289,9 +308,9 @@ static ssize_t flags_store(struct device *dev, struct device_attribute *attr,
}
NETDEVICE_SHOW_RW(flags, fmt_hex);
static int change_tx_queue_len(struct net_device *net, unsigned long new_len)
static int change_tx_queue_len(struct net_device *dev, unsigned long new_len)
{
net->tx_queue_len = new_len;
dev->tx_queue_len = new_len;
return 0;
}
@ -344,9 +363,9 @@ static ssize_t ifalias_show(struct device *dev,
}
static DEVICE_ATTR_RW(ifalias);
static int change_group(struct net_device *net, unsigned long new_group)
static int change_group(struct net_device *dev, unsigned long new_group)
{
dev_set_group(net, (int) new_group);
dev_set_group(dev, (int) new_group);
return 0;
}
@ -387,6 +406,7 @@ static struct attribute *net_class_attrs[] = {
&dev_attr_dev_port.attr,
&dev_attr_iflink.attr,
&dev_attr_ifindex.attr,
&dev_attr_name_assign_type.attr,
&dev_attr_addr_assign_type.attr,
&dev_attr_addr_len.attr,
&dev_attr_link_mode.attr,
@ -776,20 +796,20 @@ static struct kobj_type rx_queue_ktype = {
.namespace = rx_queue_namespace
};
static int rx_queue_add_kobject(struct net_device *net, int index)
static int rx_queue_add_kobject(struct net_device *dev, int index)
{
struct netdev_rx_queue *queue = net->_rx + index;
struct netdev_rx_queue *queue = dev->_rx + index;
struct kobject *kobj = &queue->kobj;
int error = 0;
kobj->kset = net->queues_kset;
kobj->kset = dev->queues_kset;
error = kobject_init_and_add(kobj, &rx_queue_ktype, NULL,
"rx-%u", index);
if (error)
goto exit;
if (net->sysfs_rx_queue_group) {
error = sysfs_create_group(kobj, net->sysfs_rx_queue_group);
if (dev->sysfs_rx_queue_group) {
error = sysfs_create_group(kobj, dev->sysfs_rx_queue_group);
if (error)
goto exit;
}
@ -805,18 +825,18 @@ exit:
#endif /* CONFIG_SYSFS */
int
net_rx_queue_update_kobjects(struct net_device *net, int old_num, int new_num)
net_rx_queue_update_kobjects(struct net_device *dev, int old_num, int new_num)
{
#ifdef CONFIG_SYSFS
int i;
int error = 0;
#ifndef CONFIG_RPS
if (!net->sysfs_rx_queue_group)
if (!dev->sysfs_rx_queue_group)
return 0;
#endif
for (i = old_num; i < new_num; i++) {
error = rx_queue_add_kobject(net, i);
error = rx_queue_add_kobject(dev, i);
if (error) {
new_num = old_num;
break;
@ -824,10 +844,10 @@ net_rx_queue_update_kobjects(struct net_device *net, int old_num, int new_num)
}
while (--i >= new_num) {
if (net->sysfs_rx_queue_group)
sysfs_remove_group(&net->_rx[i].kobj,
net->sysfs_rx_queue_group);
kobject_put(&net->_rx[i].kobj);
if (dev->sysfs_rx_queue_group)
sysfs_remove_group(&dev->_rx[i].kobj,
dev->sysfs_rx_queue_group);
kobject_put(&dev->_rx[i].kobj);
}
return error;
@ -1135,13 +1155,13 @@ static struct kobj_type netdev_queue_ktype = {
.namespace = netdev_queue_namespace,
};
static int netdev_queue_add_kobject(struct net_device *net, int index)
static int netdev_queue_add_kobject(struct net_device *dev, int index)
{
struct netdev_queue *queue = net->_tx + index;
struct netdev_queue *queue = dev->_tx + index;
struct kobject *kobj = &queue->kobj;
int error = 0;
kobj->kset = net->queues_kset;
kobj->kset = dev->queues_kset;
error = kobject_init_and_add(kobj, &netdev_queue_ktype, NULL,
"tx-%u", index);
if (error)
@ -1164,14 +1184,14 @@ exit:
#endif /* CONFIG_SYSFS */
int
netdev_queue_update_kobjects(struct net_device *net, int old_num, int new_num)
netdev_queue_update_kobjects(struct net_device *dev, int old_num, int new_num)
{
#ifdef CONFIG_SYSFS
int i;
int error = 0;
for (i = old_num; i < new_num; i++) {
error = netdev_queue_add_kobject(net, i);
error = netdev_queue_add_kobject(dev, i);
if (error) {
new_num = old_num;
break;
@ -1179,7 +1199,7 @@ netdev_queue_update_kobjects(struct net_device *net, int old_num, int new_num)
}
while (--i >= new_num) {
struct netdev_queue *queue = net->_tx + i;
struct netdev_queue *queue = dev->_tx + i;
#ifdef CONFIG_BQL
sysfs_remove_group(&queue->kobj, &dql_group);
@ -1193,25 +1213,25 @@ netdev_queue_update_kobjects(struct net_device *net, int old_num, int new_num)
#endif /* CONFIG_SYSFS */
}
static int register_queue_kobjects(struct net_device *net)
static int register_queue_kobjects(struct net_device *dev)
{
int error = 0, txq = 0, rxq = 0, real_rx = 0, real_tx = 0;
#ifdef CONFIG_SYSFS
net->queues_kset = kset_create_and_add("queues",
NULL, &net->dev.kobj);
if (!net->queues_kset)
dev->queues_kset = kset_create_and_add("queues",
NULL, &dev->dev.kobj);
if (!dev->queues_kset)
return -ENOMEM;
real_rx = net->real_num_rx_queues;
real_rx = dev->real_num_rx_queues;
#endif
real_tx = net->real_num_tx_queues;
real_tx = dev->real_num_tx_queues;
error = net_rx_queue_update_kobjects(net, 0, real_rx);
error = net_rx_queue_update_kobjects(dev, 0, real_rx);
if (error)
goto error;
rxq = real_rx;
error = netdev_queue_update_kobjects(net, 0, real_tx);
error = netdev_queue_update_kobjects(dev, 0, real_tx);
if (error)
goto error;
txq = real_tx;
@ -1219,24 +1239,24 @@ static int register_queue_kobjects(struct net_device *net)
return 0;
error:
netdev_queue_update_kobjects(net, txq, 0);
net_rx_queue_update_kobjects(net, rxq, 0);
netdev_queue_update_kobjects(dev, txq, 0);
net_rx_queue_update_kobjects(dev, rxq, 0);
return error;
}
static void remove_queue_kobjects(struct net_device *net)
static void remove_queue_kobjects(struct net_device *dev)
{
int real_rx = 0, real_tx = 0;
#ifdef CONFIG_SYSFS
real_rx = net->real_num_rx_queues;
real_rx = dev->real_num_rx_queues;
#endif
real_tx = net->real_num_tx_queues;
real_tx = dev->real_num_tx_queues;
net_rx_queue_update_kobjects(net, real_rx, 0);
netdev_queue_update_kobjects(net, real_tx, 0);
net_rx_queue_update_kobjects(dev, real_rx, 0);
netdev_queue_update_kobjects(dev, real_tx, 0);
#ifdef CONFIG_SYSFS
kset_unregister(net->queues_kset);
kset_unregister(dev->queues_kset);
#endif
}
@ -1329,13 +1349,13 @@ static struct class net_class = {
/* Delete sysfs entries but hold kobject reference until after all
* netdev references are gone.
*/
void netdev_unregister_kobject(struct net_device * net)
void netdev_unregister_kobject(struct net_device *ndev)
{
struct device *dev = &(net->dev);
struct device *dev = &(ndev->dev);
kobject_get(&dev->kobj);
remove_queue_kobjects(net);
remove_queue_kobjects(ndev);
pm_runtime_set_memalloc_noio(dev, false);
@ -1343,18 +1363,18 @@ void netdev_unregister_kobject(struct net_device * net)
}
/* Create sysfs entries for network device. */
int netdev_register_kobject(struct net_device *net)
int netdev_register_kobject(struct net_device *ndev)
{
struct device *dev = &(net->dev);
const struct attribute_group **groups = net->sysfs_groups;
struct device *dev = &(ndev->dev);
const struct attribute_group **groups = ndev->sysfs_groups;
int error = 0;
device_initialize(dev);
dev->class = &net_class;
dev->platform_data = net;
dev->platform_data = ndev;
dev->groups = groups;
dev_set_name(dev, "%s", net->name);
dev_set_name(dev, "%s", ndev->name);
#ifdef CONFIG_SYSFS
/* Allow for a device specific group */
@ -1364,10 +1384,10 @@ int netdev_register_kobject(struct net_device *net)
*groups++ = &netstat_group;
#if IS_ENABLED(CONFIG_WIRELESS_EXT) || IS_ENABLED(CONFIG_CFG80211)
if (net->ieee80211_ptr)
if (ndev->ieee80211_ptr)
*groups++ = &wireless_group;
#if IS_ENABLED(CONFIG_WIRELESS_EXT)
else if (net->wireless_handlers)
else if (ndev->wireless_handlers)
*groups++ = &wireless_group;
#endif
#endif
@ -1377,7 +1397,7 @@ int netdev_register_kobject(struct net_device *net)
if (error)
return error;
error = register_queue_kobjects(net);
error = register_queue_kobjects(ndev);
if (error) {
device_del(dev);
return error;

View file

@ -822,7 +822,8 @@ void __netpoll_cleanup(struct netpoll *np)
RCU_INIT_POINTER(np->dev->npinfo, NULL);
call_rcu_bh(&npinfo->rcu, rcu_cleanup_netpoll_info);
}
} else
RCU_INIT_POINTER(np->dev->npinfo, NULL);
}
EXPORT_SYMBOL_GPL(__netpoll_cleanup);

View file

@ -69,8 +69,9 @@
* for running devices in the if_list and sends packets until count is 0 it
* also the thread checks the thread->control which is used for inter-process
* communication. controlling process "posts" operations to the threads this
* way. The if_lock should be possible to remove when add/rem_device is merged
* into this too.
* way.
* The if_list is RCU protected, and the if_lock remains to protect updating
* of if_list, from "add_device" as it invoked from userspace (via proc write).
*
* By design there should only be *one* "controlling" process. In practice
* multiple write accesses gives unpredictable result. Understood by "write"
@ -208,7 +209,7 @@
#define T_REMDEVALL (1<<2) /* Remove all devs */
#define T_REMDEV (1<<3) /* Remove one dev */
/* If lock -- can be removed after some work */
/* If lock -- protects updating of if_list */
#define if_lock(t) spin_lock(&(t->if_lock));
#define if_unlock(t) spin_unlock(&(t->if_lock));
@ -241,6 +242,7 @@ struct pktgen_dev {
struct proc_dir_entry *entry; /* proc file */
struct pktgen_thread *pg_thread;/* the owner */
struct list_head list; /* chaining in the thread's run-queue */
struct rcu_head rcu; /* freed by RCU */
int running; /* if false, the test will stop */
@ -802,7 +804,6 @@ static int strn_len(const char __user * user_buffer, unsigned int maxlen)
case '\t':
case ' ':
goto done_str;
break;
default:
break;
}
@ -1737,14 +1738,14 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)
seq_puts(seq, "Running: ");
if_lock(t);
list_for_each_entry(pkt_dev, &t->if_list, list)
rcu_read_lock();
list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
if (pkt_dev->running)
seq_printf(seq, "%s ", pkt_dev->odevname);
seq_puts(seq, "\nStopped: ");
list_for_each_entry(pkt_dev, &t->if_list, list)
list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
if (!pkt_dev->running)
seq_printf(seq, "%s ", pkt_dev->odevname);
@ -1753,7 +1754,7 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)
else
seq_puts(seq, "\nResult: NA\n");
if_unlock(t);
rcu_read_unlock();
return 0;
}
@ -1878,10 +1879,8 @@ static struct pktgen_dev *__pktgen_NN_threads(const struct pktgen_net *pn,
pkt_dev = pktgen_find_dev(t, ifname, exact);
if (pkt_dev) {
if (remove) {
if_lock(t);
pkt_dev->removal_mark = 1;
t->control |= T_REMDEV;
if_unlock(t);
}
break;
}
@ -1931,7 +1930,8 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d
list_for_each_entry(t, &pn->pktgen_threads, th_list) {
struct pktgen_dev *pkt_dev;
list_for_each_entry(pkt_dev, &t->if_list, list) {
rcu_read_lock();
list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
if (pkt_dev->odev != dev)
continue;
@ -1946,6 +1946,7 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d
dev->name);
break;
}
rcu_read_unlock();
}
}
@ -2997,8 +2998,8 @@ static void pktgen_run(struct pktgen_thread *t)
func_enter();
if_lock(t);
list_for_each_entry(pkt_dev, &t->if_list, list) {
rcu_read_lock();
list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
/*
* setup odev and create initial packet.
@ -3007,18 +3008,18 @@ static void pktgen_run(struct pktgen_thread *t)
if (pkt_dev->odev) {
pktgen_clear_counters(pkt_dev);
pkt_dev->running = 1; /* Cranke yeself! */
pkt_dev->skb = NULL;
pkt_dev->started_at = pkt_dev->next_tx = ktime_get();
set_pkt_overhead(pkt_dev);
strcpy(pkt_dev->result, "Starting");
pkt_dev->running = 1; /* Cranke yeself! */
started++;
} else
strcpy(pkt_dev->result, "Error starting");
}
if_unlock(t);
rcu_read_unlock();
if (started)
t->control &= ~(T_STOP);
}
@ -3041,27 +3042,25 @@ static int thread_is_running(const struct pktgen_thread *t)
{
const struct pktgen_dev *pkt_dev;
list_for_each_entry(pkt_dev, &t->if_list, list)
if (pkt_dev->running)
rcu_read_lock();
list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
if (pkt_dev->running) {
rcu_read_unlock();
return 1;
}
rcu_read_unlock();
return 0;
}
static int pktgen_wait_thread_run(struct pktgen_thread *t)
{
if_lock(t);
while (thread_is_running(t)) {
if_unlock(t);
msleep_interruptible(100);
if (signal_pending(current))
goto signal;
if_lock(t);
}
if_unlock(t);
return 1;
signal:
return 0;
@ -3166,10 +3165,10 @@ static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
return -EINVAL;
}
pkt_dev->running = 0;
kfree_skb(pkt_dev->skb);
pkt_dev->skb = NULL;
pkt_dev->stopped_at = ktime_get();
pkt_dev->running = 0;
show_results(pkt_dev, nr_frags);
@ -3180,9 +3179,8 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
{
struct pktgen_dev *pkt_dev, *best = NULL;
if_lock(t);
list_for_each_entry(pkt_dev, &t->if_list, list) {
rcu_read_lock();
list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
if (!pkt_dev->running)
continue;
if (best == NULL)
@ -3190,7 +3188,8 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0)
best = pkt_dev;
}
if_unlock(t);
rcu_read_unlock();
return best;
}
@ -3200,13 +3199,13 @@ static void pktgen_stop(struct pktgen_thread *t)
func_enter();
if_lock(t);
rcu_read_lock();
list_for_each_entry(pkt_dev, &t->if_list, list) {
list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
pktgen_stop_device(pkt_dev);
}
if_unlock(t);
rcu_read_unlock();
}
/*
@ -3220,8 +3219,6 @@ static void pktgen_rem_one_if(struct pktgen_thread *t)
func_enter();
if_lock(t);
list_for_each_safe(q, n, &t->if_list) {
cur = list_entry(q, struct pktgen_dev, list);
@ -3235,8 +3232,6 @@ static void pktgen_rem_one_if(struct pktgen_thread *t)
break;
}
if_unlock(t);
}
static void pktgen_rem_all_ifs(struct pktgen_thread *t)
@ -3248,8 +3243,6 @@ static void pktgen_rem_all_ifs(struct pktgen_thread *t)
/* Remove all devices, free mem */
if_lock(t);
list_for_each_safe(q, n, &t->if_list) {
cur = list_entry(q, struct pktgen_dev, list);
@ -3258,8 +3251,6 @@ static void pktgen_rem_all_ifs(struct pktgen_thread *t)
pktgen_remove_device(t, cur);
}
if_unlock(t);
}
static void pktgen_rem_thread(struct pktgen_thread *t)
@ -3407,10 +3398,10 @@ static int pktgen_thread_worker(void *arg)
pr_debug("starting pktgen/%d: pid=%d\n", cpu, task_pid_nr(current));
set_current_state(TASK_INTERRUPTIBLE);
set_freezable();
__set_current_state(TASK_RUNNING);
while (!kthread_should_stop()) {
pkt_dev = next_to_run(t);
@ -3424,8 +3415,6 @@ static int pktgen_thread_worker(void *arg)
continue;
}
__set_current_state(TASK_RUNNING);
if (likely(pkt_dev)) {
pktgen_xmit(pkt_dev);
@ -3456,9 +3445,8 @@ static int pktgen_thread_worker(void *arg)
}
try_to_freeze();
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_INTERRUPTIBLE);
pr_debug("%s stopping all device\n", t->tsk->comm);
pktgen_stop(t);
@ -3485,8 +3473,8 @@ static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
struct pktgen_dev *p, *pkt_dev = NULL;
size_t len = strlen(ifname);
if_lock(t);
list_for_each_entry(p, &t->if_list, list)
rcu_read_lock();
list_for_each_entry_rcu(p, &t->if_list, list)
if (strncmp(p->odevname, ifname, len) == 0) {
if (p->odevname[len]) {
if (exact || p->odevname[len] != '@')
@ -3496,7 +3484,7 @@ static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
break;
}
if_unlock(t);
rcu_read_unlock();
pr_debug("find_dev(%s) returning %p\n", ifname, pkt_dev);
return pkt_dev;
}
@ -3510,6 +3498,12 @@ static int add_dev_to_thread(struct pktgen_thread *t,
{
int rv = 0;
/* This function cannot be called concurrently, as its called
* under pktgen_thread_lock mutex, but it can run from
* userspace on another CPU than the kthread. The if_lock()
* is used here to sync with concurrent instances of
* _rem_dev_from_if_list() invoked via kthread, which is also
* updating the if_list */
if_lock(t);
if (pkt_dev->pg_thread) {
@ -3518,9 +3512,9 @@ static int add_dev_to_thread(struct pktgen_thread *t,
goto out;
}
list_add(&pkt_dev->list, &t->if_list);
pkt_dev->pg_thread = t;
pkt_dev->running = 0;
pkt_dev->pg_thread = t;
list_add_rcu(&pkt_dev->list, &t->if_list);
out:
if_unlock(t);
@ -3675,11 +3669,13 @@ static void _rem_dev_from_if_list(struct pktgen_thread *t,
struct list_head *q, *n;
struct pktgen_dev *p;
if_lock(t);
list_for_each_safe(q, n, &t->if_list) {
p = list_entry(q, struct pktgen_dev, list);
if (p == pkt_dev)
list_del(&p->list);
list_del_rcu(&p->list);
}
if_unlock(t);
}
static int pktgen_remove_device(struct pktgen_thread *t,
@ -3699,20 +3695,22 @@ static int pktgen_remove_device(struct pktgen_thread *t,
pkt_dev->odev = NULL;
}
/* And update the thread if_list */
_rem_dev_from_if_list(t, pkt_dev);
/* Remove proc before if_list entry, because add_device uses
* list to determine if interface already exist, avoid race
* with proc_create_data() */
if (pkt_dev->entry)
proc_remove(pkt_dev->entry);
/* And update the thread if_list */
_rem_dev_from_if_list(t, pkt_dev);
#ifdef CONFIG_XFRM
free_SAs(pkt_dev);
#endif
vfree(pkt_dev->flows);
if (pkt_dev->page)
put_page(pkt_dev->page);
kfree(pkt_dev);
kfree_rcu(pkt_dev, rcu);
return 0;
}
@ -3812,6 +3810,7 @@ static void __exit pg_cleanup(void)
{
unregister_netdevice_notifier(&pktgen_notifier_block);
unregister_pernet_subsys(&pg_net_ops);
/* Don't need rcu_barrier() due to use of kfree_rcu() */
}
module_init(pg_init);

View file

@ -52,15 +52,44 @@
* test_8021q:
* jneq #0x8100, test_ieee1588 ; ETH_P_8021Q ?
* ldh [16] ; load inner type
* jneq #0x88f7, drop_ieee1588 ; ETH_P_1588 ?
* jneq #0x88f7, test_8021q_ipv4 ; ETH_P_1588 ?
* ldb [18] ; load payload
* and #0x8 ; as we don't have ports here, test
* jneq #0x0, drop_ieee1588 ; for PTP_GEN_BIT and drop these
* ldh [18] ; reload payload
* and #0xf ; mask PTP_CLASS_VMASK
* or #0x40 ; PTP_CLASS_V2_VLAN
* or #0x70 ; PTP_CLASS_VLAN|PTP_CLASS_L2
* ret a ; return PTP class
*
* ; PTP over UDP over IPv4 over 802.1Q over Ethernet
* test_8021q_ipv4:
* jneq #0x800, test_8021q_ipv6 ; ETH_P_IP ?
* ldb [27] ; load proto
* jneq #17, drop_8021q_ipv4 ; IPPROTO_UDP ?
* ldh [24] ; load frag offset field
* jset #0x1fff, drop_8021q_ipv4; don't allow fragments
* ldxb 4*([18]&0xf) ; load IP header len
* ldh [x + 20] ; load UDP dst port
* jneq #319, drop_8021q_ipv4 ; is port PTP_EV_PORT ?
* ldh [x + 26] ; load payload
* and #0xf ; mask PTP_CLASS_VMASK
* or #0x50 ; PTP_CLASS_VLAN|PTP_CLASS_IPV4
* ret a ; return PTP class
* drop_8021q_ipv4: ret #0x0 ; PTP_CLASS_NONE
*
* ; PTP over UDP over IPv6 over 802.1Q over Ethernet
* test_8021q_ipv6:
* jneq #0x86dd, drop_8021q_ipv6 ; ETH_P_IPV6 ?
* ldb [24] ; load proto
* jneq #17, drop_8021q_ipv6 ; IPPROTO_UDP ?
* ldh [60] ; load UDP dst port
* jneq #319, drop_8021q_ipv6 ; is port PTP_EV_PORT ?
* ldh [66] ; load payload
* and #0xf ; mask PTP_CLASS_VMASK
* or #0x60 ; PTP_CLASS_VLAN|PTP_CLASS_IPV6
* ret a ; return PTP class
* drop_8021q_ipv6: ret #0x0 ; PTP_CLASS_NONE
*
* ; PTP over Ethernet
* test_ieee1588:
* jneq #0x88f7, drop_ieee1588 ; ETH_P_1588 ?
@ -78,11 +107,11 @@
#include <linux/filter.h>
#include <linux/ptp_classify.h>
static struct sk_filter *ptp_insns __read_mostly;
static struct bpf_prog *ptp_insns __read_mostly;
unsigned int ptp_classify_raw(const struct sk_buff *skb)
{
return SK_RUN_FILTER(ptp_insns, skb);
return BPF_PROG_RUN(ptp_insns, skb);
}
EXPORT_SYMBOL_GPL(ptp_classify_raw);
@ -113,16 +142,39 @@ void __init ptp_classifier_init(void)
{ 0x44, 0, 0, 0x00000020 },
{ 0x16, 0, 0, 0x00000000 },
{ 0x06, 0, 0, 0x00000000 },
{ 0x15, 0, 9, 0x00008100 },
{ 0x15, 0, 32, 0x00008100 },
{ 0x28, 0, 0, 0x00000010 },
{ 0x15, 0, 15, 0x000088f7 },
{ 0x15, 0, 7, 0x000088f7 },
{ 0x30, 0, 0, 0x00000012 },
{ 0x54, 0, 0, 0x00000008 },
{ 0x15, 0, 12, 0x00000000 },
{ 0x15, 0, 35, 0x00000000 },
{ 0x28, 0, 0, 0x00000012 },
{ 0x54, 0, 0, 0x0000000f },
{ 0x44, 0, 0, 0x00000040 },
{ 0x44, 0, 0, 0x00000070 },
{ 0x16, 0, 0, 0x00000000 },
{ 0x15, 0, 12, 0x00000800 },
{ 0x30, 0, 0, 0x0000001b },
{ 0x15, 0, 9, 0x00000011 },
{ 0x28, 0, 0, 0x00000018 },
{ 0x45, 7, 0, 0x00001fff },
{ 0xb1, 0, 0, 0x00000012 },
{ 0x48, 0, 0, 0x00000014 },
{ 0x15, 0, 4, 0x0000013f },
{ 0x48, 0, 0, 0x0000001a },
{ 0x54, 0, 0, 0x0000000f },
{ 0x44, 0, 0, 0x00000050 },
{ 0x16, 0, 0, 0x00000000 },
{ 0x06, 0, 0, 0x00000000 },
{ 0x15, 0, 8, 0x000086dd },
{ 0x30, 0, 0, 0x00000018 },
{ 0x15, 0, 6, 0x00000011 },
{ 0x28, 0, 0, 0x0000003c },
{ 0x15, 0, 4, 0x0000013f },
{ 0x28, 0, 0, 0x00000042 },
{ 0x54, 0, 0, 0x0000000f },
{ 0x44, 0, 0, 0x00000060 },
{ 0x16, 0, 0, 0x00000000 },
{ 0x06, 0, 0, 0x00000000 },
{ 0x15, 0, 7, 0x000088f7 },
{ 0x30, 0, 0, 0x0000000e },
{ 0x54, 0, 0, 0x00000008 },
@ -137,5 +189,5 @@ void __init ptp_classifier_init(void)
.len = ARRAY_SIZE(ptp_filter), .filter = ptp_filter,
};
BUG_ON(sk_unattached_filter_create(&ptp_insns, &ptp_prog));
BUG_ON(bpf_prog_create(&ptp_insns, &ptp_prog));
}

View file

@ -41,27 +41,27 @@ int reqsk_queue_alloc(struct request_sock_queue *queue,
unsigned int nr_table_entries)
{
size_t lopt_size = sizeof(struct listen_sock);
struct listen_sock *lopt;
struct listen_sock *lopt = NULL;
nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);
nr_table_entries = max_t(u32, nr_table_entries, 8);
nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);
lopt_size += nr_table_entries * sizeof(struct request_sock *);
if (lopt_size > PAGE_SIZE)
lopt = vzalloc(lopt_size);
else
lopt = kzalloc(lopt_size, GFP_KERNEL);
if (lopt == NULL)
return -ENOMEM;
for (lopt->max_qlen_log = 3;
(1 << lopt->max_qlen_log) < nr_table_entries;
lopt->max_qlen_log++);
if (lopt_size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER))
lopt = kzalloc(lopt_size, GFP_KERNEL |
__GFP_NOWARN |
__GFP_NORETRY);
if (!lopt)
lopt = vzalloc(lopt_size);
if (!lopt)
return -ENOMEM;
get_random_bytes(&lopt->hash_rnd, sizeof(lopt->hash_rnd));
rwlock_init(&queue->syn_wait_lock);
queue->rskq_accept_head = NULL;
lopt->nr_table_entries = nr_table_entries;
lopt->max_qlen_log = ilog2(nr_table_entries);
write_lock_bh(&queue->syn_wait_lock);
queue->listen_opt = lopt;
@ -72,22 +72,8 @@ int reqsk_queue_alloc(struct request_sock_queue *queue,
void __reqsk_queue_destroy(struct request_sock_queue *queue)
{
struct listen_sock *lopt;
size_t lopt_size;
/*
* this is an error recovery path only
* no locking needed and the lopt is not NULL
*/
lopt = queue->listen_opt;
lopt_size = sizeof(struct listen_sock) +
lopt->nr_table_entries * sizeof(struct request_sock *);
if (lopt_size > PAGE_SIZE)
vfree(lopt);
else
kfree(lopt);
/* This is an error recovery path only, no locking needed */
kvfree(queue->listen_opt);
}
static inline struct listen_sock *reqsk_queue_yank_listen_sk(
@ -107,8 +93,6 @@ void reqsk_queue_destroy(struct request_sock_queue *queue)
{
/* make all the listen_opt local to us */
struct listen_sock *lopt = reqsk_queue_yank_listen_sk(queue);
size_t lopt_size = sizeof(struct listen_sock) +
lopt->nr_table_entries * sizeof(struct request_sock *);
if (lopt->qlen != 0) {
unsigned int i;
@ -125,10 +109,7 @@ void reqsk_queue_destroy(struct request_sock_queue *queue)
}
WARN_ON(lopt->qlen != 0);
if (lopt_size > PAGE_SIZE)
vfree(lopt);
else
kfree(lopt);
kvfree(lopt);
}
/*

View file

@ -299,7 +299,12 @@ int __rtnl_link_register(struct rtnl_link_ops *ops)
if (rtnl_link_ops_get(ops->kind))
return -EEXIST;
if (!ops->dellink)
/* The check for setup is here because if ops
* does not have that filled up, it is not possible
* to use the ops for creating device. So do not
* fill up dellink as well. That disables rtnl_dellink.
*/
if (ops->setup && !ops->dellink)
ops->dellink = unregister_netdevice_queue;
list_add_tail(&ops->list, &link_ops);
@ -1777,7 +1782,7 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
return -ENODEV;
ops = dev->rtnl_link_ops;
if (!ops)
if (!ops || !ops->dellink)
return -EOPNOTSUPP;
ops->dellink(dev, &list_kill);
@ -1805,7 +1810,8 @@ 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 *net,
char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[])
char *ifname, unsigned char name_assign_type,
const struct rtnl_link_ops *ops, struct nlattr *tb[])
{
int err;
struct net_device *dev;
@ -1823,8 +1829,8 @@ struct net_device *rtnl_create_link(struct net *net,
num_rx_queues = ops->get_num_rx_queues();
err = -ENOMEM;
dev = alloc_netdev_mqs(ops->priv_size, ifname, ops->setup,
num_tx_queues, num_rx_queues);
dev = alloc_netdev_mqs(ops->priv_size, ifname, name_assign_type,
ops->setup, num_tx_queues, num_rx_queues);
if (!dev)
goto err;
@ -1889,6 +1895,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh)
char ifname[IFNAMSIZ];
struct nlattr *tb[IFLA_MAX+1];
struct nlattr *linkinfo[IFLA_INFO_MAX+1];
unsigned char name_assign_type = NET_NAME_USER;
int err;
#ifdef CONFIG_MODULES
@ -2038,14 +2045,19 @@ replay:
return -EOPNOTSUPP;
}
if (!ifname[0])
if (!ops->setup)
return -EOPNOTSUPP;
if (!ifname[0]) {
snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
name_assign_type = NET_NAME_ENUM;
}
dest_net = rtnl_link_get_net(net, tb);
if (IS_ERR(dest_net))
return PTR_ERR(dest_net);
dev = rtnl_create_link(dest_net, ifname, ops, tb);
dev = rtnl_create_link(dest_net, ifname, name_assign_type, ops, tb);
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
goto out;
@ -2380,22 +2392,20 @@ int ndo_dflt_fdb_del(struct ndmsg *ndm,
struct net_device *dev,
const unsigned char *addr)
{
int err = -EOPNOTSUPP;
int err = -EINVAL;
/* If aging addresses are supported device will need to
* implement its own handler for this.
*/
if (!(ndm->ndm_state & NUD_PERMANENT)) {
pr_info("%s: FDB only supports static addresses\n", dev->name);
return -EINVAL;
return err;
}
if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
err = dev_uc_del(dev, addr);
else if (is_multicast_ether_addr(addr))
err = dev_mc_del(dev, addr);
else
err = -EINVAL;
return err;
}
@ -2509,6 +2519,7 @@ skip:
int ndo_dflt_fdb_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev,
int idx)
{
int err;
@ -2526,28 +2537,72 @@ EXPORT_SYMBOL(ndo_dflt_fdb_dump);
static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
int idx = 0;
struct net *net = sock_net(skb->sk);
struct net_device *dev;
struct nlattr *tb[IFLA_MAX+1];
struct net_device *bdev = NULL;
struct net_device *br_dev = NULL;
const struct net_device_ops *ops = NULL;
const struct net_device_ops *cops = NULL;
struct ifinfomsg *ifm = nlmsg_data(cb->nlh);
struct net *net = sock_net(skb->sk);
int brport_idx = 0;
int br_idx = 0;
int idx = 0;
rcu_read_lock();
for_each_netdev_rcu(net, dev) {
if (dev->priv_flags & IFF_BRIDGE_PORT) {
struct net_device *br_dev;
const struct net_device_ops *ops;
if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
ifla_policy) == 0) {
if (tb[IFLA_MASTER])
br_idx = nla_get_u32(tb[IFLA_MASTER]);
}
br_dev = netdev_master_upper_dev_get(dev);
ops = br_dev->netdev_ops;
if (ops->ndo_fdb_dump)
idx = ops->ndo_fdb_dump(skb, cb, dev, idx);
brport_idx = ifm->ifi_index;
if (br_idx) {
br_dev = __dev_get_by_index(net, br_idx);
if (!br_dev)
return -ENODEV;
ops = br_dev->netdev_ops;
bdev = br_dev;
}
for_each_netdev(net, dev) {
if (brport_idx && (dev->ifindex != brport_idx))
continue;
if (!br_idx) { /* user did not specify a specific bridge */
if (dev->priv_flags & IFF_BRIDGE_PORT) {
br_dev = netdev_master_upper_dev_get(dev);
cops = br_dev->netdev_ops;
}
bdev = dev;
} else {
if (dev != br_dev &&
!(dev->priv_flags & IFF_BRIDGE_PORT))
continue;
if (br_dev != netdev_master_upper_dev_get(dev) &&
!(dev->priv_flags & IFF_EBRIDGE))
continue;
bdev = br_dev;
cops = ops;
}
if (dev->priv_flags & IFF_BRIDGE_PORT) {
if (cops && cops->ndo_fdb_dump)
idx = cops->ndo_fdb_dump(skb, cb, br_dev, dev,
idx);
}
idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx);
if (dev->netdev_ops->ndo_fdb_dump)
idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx);
else
idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, bdev, dev,
idx);
cops = NULL;
}
rcu_read_unlock();
cb->args[0] = idx;
return skb->len;

View file

@ -2976,9 +2976,9 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
tail = nskb;
__copy_skb_header(nskb, head_skb);
nskb->mac_len = head_skb->mac_len;
skb_headers_offset_update(nskb, skb_headroom(nskb) - headroom);
skb_reset_mac_len(nskb);
skb_copy_from_linear_data_offset(head_skb, -tnl_hlen,
nskb->data - tnl_hlen,
@ -3490,10 +3490,10 @@ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)
}
EXPORT_SYMBOL(sock_queue_err_skb);
void skb_tstamp_tx(struct sk_buff *orig_skb,
struct skb_shared_hwtstamps *hwtstamps)
void __skb_tstamp_tx(struct sk_buff *orig_skb,
struct skb_shared_hwtstamps *hwtstamps,
struct sock *sk, int tstype)
{
struct sock *sk = orig_skb->sk;
struct sock_exterr_skb *serr;
struct sk_buff *skb;
int err;
@ -3521,12 +3521,26 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
memset(serr, 0, sizeof(*serr));
serr->ee.ee_errno = ENOMSG;
serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING;
serr->ee.ee_info = tstype;
if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) {
serr->ee.ee_data = skb_shinfo(skb)->tskey;
if (sk->sk_protocol == IPPROTO_TCP)
serr->ee.ee_data -= sk->sk_tskey;
}
err = sock_queue_err_skb(sk, skb);
if (err)
kfree_skb(skb);
}
EXPORT_SYMBOL_GPL(__skb_tstamp_tx);
void skb_tstamp_tx(struct sk_buff *orig_skb,
struct skb_shared_hwtstamps *hwtstamps)
{
return __skb_tstamp_tx(orig_skb, hwtstamps, orig_skb->sk,
SCM_TSTAMP_SND);
}
EXPORT_SYMBOL_GPL(skb_tstamp_tx);
void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)

View file

@ -491,7 +491,7 @@ int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
skb->dev = NULL;
if (sk_rcvqueues_full(sk, skb, sk->sk_rcvbuf)) {
if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
atomic_inc(&sk->sk_drops);
goto discard_and_relse;
}
@ -848,24 +848,25 @@ set_rcvbuf:
ret = -EINVAL;
break;
}
sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE,
val & SOF_TIMESTAMPING_TX_HARDWARE);
sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE,
val & SOF_TIMESTAMPING_TX_SOFTWARE);
sock_valbool_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE,
val & SOF_TIMESTAMPING_RX_HARDWARE);
if (val & SOF_TIMESTAMPING_OPT_ID &&
!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) {
if (sk->sk_protocol == IPPROTO_TCP) {
if (sk->sk_state != TCP_ESTABLISHED) {
ret = -EINVAL;
break;
}
sk->sk_tskey = tcp_sk(sk)->snd_una;
} else {
sk->sk_tskey = 0;
}
}
sk->sk_tsflags = val;
if (val & SOF_TIMESTAMPING_RX_SOFTWARE)
sock_enable_timestamp(sk,
SOCK_TIMESTAMPING_RX_SOFTWARE);
else
sock_disable_timestamp(sk,
(1UL << SOCK_TIMESTAMPING_RX_SOFTWARE));
sock_valbool_flag(sk, SOCK_TIMESTAMPING_SOFTWARE,
val & SOF_TIMESTAMPING_SOFTWARE);
sock_valbool_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE,
val & SOF_TIMESTAMPING_SYS_HARDWARE);
sock_valbool_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE,
val & SOF_TIMESTAMPING_RAW_HARDWARE);
break;
case SO_RCVLOWAT:
@ -1091,21 +1092,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
break;
case SO_TIMESTAMPING:
v.val = 0;
if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE))
v.val |= SOF_TIMESTAMPING_TX_HARDWARE;
if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE))
v.val |= SOF_TIMESTAMPING_TX_SOFTWARE;
if (sock_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE))
v.val |= SOF_TIMESTAMPING_RX_HARDWARE;
if (sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE))
v.val |= SOF_TIMESTAMPING_RX_SOFTWARE;
if (sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE))
v.val |= SOF_TIMESTAMPING_SOFTWARE;
if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE))
v.val |= SOF_TIMESTAMPING_SYS_HARDWARE;
if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE))
v.val |= SOF_TIMESTAMPING_RAW_HARDWARE;
v.val = sk->sk_tsflags;
break;
case SO_RCVTIMEO:
@ -1478,6 +1465,7 @@ static void sk_update_clone(const struct sock *sk, struct sock *newsk)
struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
{
struct sock *newsk;
bool is_charged = true;
newsk = sk_prot_alloc(sk->sk_prot, priority, sk->sk_family);
if (newsk != NULL) {
@ -1522,9 +1510,13 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
filter = rcu_dereference_protected(newsk->sk_filter, 1);
if (filter != NULL)
sk_filter_charge(newsk, filter);
/* though it's an empty new sock, the charging may fail
* if sysctl_optmem_max was changed between creation of
* original socket and cloning
*/
is_charged = sk_filter_charge(newsk, filter);
if (unlikely(xfrm_sk_clone_policy(newsk))) {
if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk))) {
/* It is still raw copy of parent, so invalidate
* destructor and make plain sk_free() */
newsk->sk_destruct = NULL;

View file

@ -68,8 +68,8 @@ int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk,
if (!filter)
goto out;
fprog = filter->orig_prog;
flen = sk_filter_proglen(fprog);
fprog = filter->prog->orig_prog;
flen = bpf_classic_proglen(fprog);
attr = nla_reserve(skb, attrtype, flen);
if (attr == NULL) {

View file

@ -43,31 +43,22 @@ void skb_clone_tx_timestamp(struct sk_buff *skb)
return;
type = classify(skb);
if (type == PTP_CLASS_NONE)
return;
switch (type) {
case PTP_CLASS_V1_IPV4:
case PTP_CLASS_V1_IPV6:
case PTP_CLASS_V2_IPV4:
case PTP_CLASS_V2_IPV6:
case PTP_CLASS_V2_L2:
case PTP_CLASS_V2_VLAN:
phydev = skb->dev->phydev;
if (likely(phydev->drv->txtstamp)) {
if (!atomic_inc_not_zero(&sk->sk_refcnt))
return;
phydev = skb->dev->phydev;
if (likely(phydev->drv->txtstamp)) {
if (!atomic_inc_not_zero(&sk->sk_refcnt))
return;
clone = skb_clone(skb, GFP_ATOMIC);
if (!clone) {
sock_put(sk);
return;
}
clone->sk = sk;
phydev->drv->txtstamp(phydev, clone, type);
clone = skb_clone(skb, GFP_ATOMIC);
if (!clone) {
sock_put(sk);
return;
}
break;
default:
break;
clone->sk = sk;
phydev->drv->txtstamp(phydev, clone, type);
}
}
EXPORT_SYMBOL_GPL(skb_clone_tx_timestamp);
@ -114,20 +105,12 @@ bool skb_defer_rx_timestamp(struct sk_buff *skb)
__skb_pull(skb, ETH_HLEN);
switch (type) {
case PTP_CLASS_V1_IPV4:
case PTP_CLASS_V1_IPV6:
case PTP_CLASS_V2_IPV4:
case PTP_CLASS_V2_IPV6:
case PTP_CLASS_V2_L2:
case PTP_CLASS_V2_VLAN:
phydev = skb->dev->phydev;
if (likely(phydev->drv->rxtstamp))
return phydev->drv->rxtstamp(phydev, skb, type);
break;
default:
break;
}
if (type == PTP_CLASS_NONE)
return false;
phydev = skb->dev->phydev;
if (likely(phydev->drv->rxtstamp))
return phydev->drv->rxtstamp(phydev, skb, type);
return false;
}

View file

@ -471,7 +471,11 @@ static int dcbnl_getapp(struct net_device *netdev, struct nlmsghdr *nlh,
id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
if (netdev->dcbnl_ops->getapp) {
up = netdev->dcbnl_ops->getapp(netdev, idtype, id);
ret = netdev->dcbnl_ops->getapp(netdev, idtype, id);
if (ret < 0)
return ret;
else
up = ret;
} else {
struct dcb_app app = {
.selector = idtype,
@ -538,6 +542,8 @@ static int dcbnl_setapp(struct net_device *netdev, struct nlmsghdr *nlh,
if (netdev->dcbnl_ops->setapp) {
ret = netdev->dcbnl_ops->setapp(netdev, idtype, id, up);
if (ret < 0)
return ret;
} else {
struct dcb_app app;
app.selector = idtype;
@ -1770,7 +1776,7 @@ EXPORT_SYMBOL(dcb_getapp);
*
* Priority 0 is an invalid priority in CEE spec. This routine
* removes applications from the app list if the priority is
* set to zero.
* set to zero. Priority is expected to be 8-bit 802.1p user priority bitmap
*/
int dcb_setapp(struct net_device *dev, struct dcb_app *new)
{
@ -1831,7 +1837,8 @@ EXPORT_SYMBOL(dcb_ieee_getapp_mask);
*
* This adds Application data to the list. Multiple application
* entries may exists for the same selector and protocol as long
* as the priorities are different.
* as the priorities are different. Priority is expected to be a
* 3-bit unsigned integer
*/
int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new)
{

View file

@ -386,7 +386,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
goto drop;
req = inet6_reqsk_alloc(&dccp6_request_sock_ops);
req = inet_reqsk_alloc(&dccp6_request_sock_ops);
if (req == NULL)
goto drop;

View file

@ -55,11 +55,9 @@ void dccp_time_wait(struct sock *sk, int state, int timeo)
const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);
#if IS_ENABLED(CONFIG_IPV6)
if (tw->tw_family == PF_INET6) {
const struct ipv6_pinfo *np = inet6_sk(sk);
tw->tw_v6_daddr = sk->sk_v6_daddr;
tw->tw_v6_rcv_saddr = sk->sk_v6_rcv_saddr;
tw->tw_ipv6only = np->ipv6only;
tw->tw_ipv6only = sk->sk_ipv6only;
}
#endif
/* Linkage updates. */

View file

@ -351,8 +351,7 @@ static void dsa_of_free_platform_data(struct dsa_platform_data *pd)
for (i = 0; i < pd->nr_chips; i++) {
port_index = 0;
while (port_index < DSA_MAX_PORTS) {
if (pd->chip[i].port_names[port_index])
kfree(pd->chip[i].port_names[port_index]);
kfree(pd->chip[i].port_names[port_index]);
port_index++;
}
kfree(pd->chip[i].rtable);

View file

@ -340,8 +340,8 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
struct dsa_slave_priv *p;
int ret;
slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv),
name, ether_setup);
slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name,
NET_NAME_UNKNOWN, ether_setup);
if (slave_dev == NULL)
return slave_dev;

View file

@ -390,7 +390,8 @@ EXPORT_SYMBOL(ether_setup);
struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
unsigned int rxqs)
{
return alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs);
return alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN,
ether_setup, txqs, rxqs);
}
EXPORT_SYMBOL(alloc_etherdev_mqs);

View file

@ -4,4 +4,5 @@
obj-$(CONFIG_HSR) += hsr.o
hsr-y := hsr_main.o hsr_framereg.o hsr_device.o hsr_netlink.o
hsr-y := hsr_main.o hsr_framereg.o hsr_device.o \
hsr_netlink.o hsr_slave.o hsr_forward.o

View file

@ -1,4 +1,4 @@
/* Copyright 2011-2013 Autronica Fire and Security AS
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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
@ -6,7 +6,7 @@
* any later version.
*
* Author(s):
* 2011-2013 Arvid Brodin, arvid.brodin@xdin.com
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*
* This file contains device methods for creating, using and destroying
* virtual HSR devices.
@ -15,12 +15,13 @@
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/pkt_sched.h>
#include "hsr_device.h"
#include "hsr_slave.h"
#include "hsr_framereg.h"
#include "hsr_main.h"
#include "hsr_forward.h"
static bool is_admin_up(struct net_device *dev)
@ -45,75 +46,108 @@ static void __hsr_set_operstate(struct net_device *dev, int transition)
}
}
void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1,
struct net_device *slave2)
static void hsr_set_operstate(struct hsr_port *master, bool has_carrier)
{
if (!is_admin_up(hsr_dev)) {
__hsr_set_operstate(hsr_dev, IF_OPER_DOWN);
if (!is_admin_up(master->dev)) {
__hsr_set_operstate(master->dev, IF_OPER_DOWN);
return;
}
if (is_slave_up(slave1) || is_slave_up(slave2))
__hsr_set_operstate(hsr_dev, IF_OPER_UP);
if (has_carrier)
__hsr_set_operstate(master->dev, IF_OPER_UP);
else
__hsr_set_operstate(hsr_dev, IF_OPER_LOWERLAYERDOWN);
__hsr_set_operstate(master->dev, IF_OPER_LOWERLAYERDOWN);
}
void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1,
struct net_device *slave2)
static bool hsr_check_carrier(struct hsr_port *master)
{
if (is_slave_up(slave1) || is_slave_up(slave2))
netif_carrier_on(hsr_dev);
struct hsr_port *port;
bool has_carrier;
has_carrier = false;
rcu_read_lock();
hsr_for_each_port(master->hsr, port)
if ((port->type != HSR_PT_MASTER) && is_slave_up(port->dev)) {
has_carrier = true;
break;
}
rcu_read_unlock();
if (has_carrier)
netif_carrier_on(master->dev);
else
netif_carrier_off(hsr_dev);
netif_carrier_off(master->dev);
return has_carrier;
}
void hsr_check_announce(struct net_device *hsr_dev, int old_operstate)
static void hsr_check_announce(struct net_device *hsr_dev,
unsigned char old_operstate)
{
struct hsr_priv *hsr_priv;
struct hsr_priv *hsr;
hsr_priv = netdev_priv(hsr_dev);
hsr = netdev_priv(hsr_dev);
if ((hsr_dev->operstate == IF_OPER_UP) && (old_operstate != IF_OPER_UP)) {
/* Went up */
hsr_priv->announce_count = 0;
hsr_priv->announce_timer.expires = jiffies +
hsr->announce_count = 0;
hsr->announce_timer.expires = jiffies +
msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL);
add_timer(&hsr_priv->announce_timer);
add_timer(&hsr->announce_timer);
}
if ((hsr_dev->operstate != IF_OPER_UP) && (old_operstate == IF_OPER_UP))
/* Went down */
del_timer(&hsr_priv->announce_timer);
del_timer(&hsr->announce_timer);
}
int hsr_get_max_mtu(struct hsr_priv *hsr_priv)
void hsr_check_carrier_and_operstate(struct hsr_priv *hsr)
{
int mtu_max;
struct hsr_port *master;
unsigned char old_operstate;
bool has_carrier;
if (hsr_priv->slave[0] && hsr_priv->slave[1])
mtu_max = min(hsr_priv->slave[0]->mtu, hsr_priv->slave[1]->mtu);
else if (hsr_priv->slave[0])
mtu_max = hsr_priv->slave[0]->mtu;
else if (hsr_priv->slave[1])
mtu_max = hsr_priv->slave[1]->mtu;
else
mtu_max = HSR_TAGLEN;
return mtu_max - HSR_TAGLEN;
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
/* netif_stacked_transfer_operstate() cannot be used here since
* it doesn't set IF_OPER_LOWERLAYERDOWN (?)
*/
old_operstate = master->dev->operstate;
has_carrier = hsr_check_carrier(master);
hsr_set_operstate(master, has_carrier);
hsr_check_announce(master->dev, old_operstate);
}
int hsr_get_max_mtu(struct hsr_priv *hsr)
{
unsigned int mtu_max;
struct hsr_port *port;
mtu_max = ETH_DATA_LEN;
rcu_read_lock();
hsr_for_each_port(hsr, port)
if (port->type != HSR_PT_MASTER)
mtu_max = min(port->dev->mtu, mtu_max);
rcu_read_unlock();
if (mtu_max < HSR_HLEN)
return 0;
return mtu_max - HSR_HLEN;
}
static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu)
{
struct hsr_priv *hsr_priv;
struct hsr_priv *hsr;
struct hsr_port *master;
hsr_priv = netdev_priv(dev);
hsr = netdev_priv(dev);
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
if (new_mtu > hsr_get_max_mtu(hsr_priv)) {
netdev_info(hsr_priv->dev, "A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets).\n",
HSR_TAGLEN);
if (new_mtu > hsr_get_max_mtu(hsr)) {
netdev_info(master->dev, "A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets).\n",
HSR_HLEN);
return -EINVAL;
}
@ -124,164 +158,95 @@ static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu)
static int hsr_dev_open(struct net_device *dev)
{
struct hsr_priv *hsr_priv;
int i;
char *slave_name;
struct hsr_priv *hsr;
struct hsr_port *port;
char designation;
hsr_priv = netdev_priv(dev);
hsr = netdev_priv(dev);
designation = '\0';
for (i = 0; i < HSR_MAX_SLAVE; i++) {
if (hsr_priv->slave[i])
slave_name = hsr_priv->slave[i]->name;
else
slave_name = "null";
if (!is_slave_up(hsr_priv->slave[i]))
netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a working HSR network\n",
'A' + i, slave_name);
rcu_read_lock();
hsr_for_each_port(hsr, port) {
if (port->type == HSR_PT_MASTER)
continue;
switch (port->type) {
case HSR_PT_SLAVE_A:
designation = 'A';
break;
case HSR_PT_SLAVE_B:
designation = 'B';
break;
default:
designation = '?';
}
if (!is_slave_up(port->dev))
netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a fully working HSR network\n",
designation, port->dev->name);
}
rcu_read_unlock();
if (designation == '\0')
netdev_warn(dev, "No slave devices configured\n");
return 0;
}
static int hsr_dev_close(struct net_device *dev)
{
/* Nothing to do here. We could try to restore the state of the slaves
* to what they were before being changed by the hsr master dev's state,
* but they might have been changed manually in the mean time too, so
* taking them up or down here might be confusing and is probably not a
* good idea.
*/
/* Nothing to do here. */
return 0;
}
static void hsr_fill_tag(struct hsr_ethhdr *hsr_ethhdr, struct hsr_priv *hsr_priv)
static netdev_features_t hsr_features_recompute(struct hsr_priv *hsr,
netdev_features_t features)
{
unsigned long irqflags;
netdev_features_t mask;
struct hsr_port *port;
/* IEC 62439-1:2010, p 48, says the 4-bit "path" field can take values
* between 0001-1001 ("ring identifier", for regular HSR frames),
* or 1111 ("HSR management", supervision frames). Unfortunately, the
* spec writers forgot to explain what a "ring identifier" is, or
* how it is used. So we just set this to 0001 for regular frames,
* and 1111 for supervision frames.
*/
set_hsr_tag_path(&hsr_ethhdr->hsr_tag, 0x1);
mask = features;
/* IEC 62439-1:2010, p 12: "The link service data unit in an Ethernet
* frame is the content of the frame located between the Length/Type
* field and the Frame Check Sequence."
/* Mask out all features that, if supported by one device, should be
* enabled for all devices (see NETIF_F_ONE_FOR_ALL).
*
* IEC 62439-3, p 48, specifies the "original LPDU" to include the
* original "LT" field (what "LT" means is not explained anywhere as
* far as I can see - perhaps "Length/Type"?). So LSDU_size might
* equal original length + 2.
* Also, the fact that this field is not used anywhere (might be used
* by a RedBox connecting HSR and PRP nets?) means I cannot test its
* correctness. Instead of guessing, I set this to 0 here, to make any
* problems immediately apparent. Anyone using this driver with PRP/HSR
* RedBoxes might need to fix this...
* Anything that's off in mask will not be enabled - so only things
* that were in features originally, and also is in NETIF_F_ONE_FOR_ALL,
* may become enabled.
*/
set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, 0);
features &= ~NETIF_F_ONE_FOR_ALL;
hsr_for_each_port(hsr, port)
features = netdev_increment_features(features,
port->dev->features,
mask);
spin_lock_irqsave(&hsr_priv->seqnr_lock, irqflags);
hsr_ethhdr->hsr_tag.sequence_nr = htons(hsr_priv->sequence_nr);
hsr_priv->sequence_nr++;
spin_unlock_irqrestore(&hsr_priv->seqnr_lock, irqflags);
hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto;
hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_PRP);
return features;
}
static int slave_xmit(struct sk_buff *skb, struct hsr_priv *hsr_priv,
enum hsr_dev_idx dev_idx)
static netdev_features_t hsr_fix_features(struct net_device *dev,
netdev_features_t features)
{
struct hsr_ethhdr *hsr_ethhdr;
struct hsr_priv *hsr = netdev_priv(dev);
hsr_ethhdr = (struct hsr_ethhdr *) skb->data;
skb->dev = hsr_priv->slave[dev_idx];
hsr_addr_subst_dest(hsr_priv, &hsr_ethhdr->ethhdr, dev_idx);
/* Address substitution (IEC62439-3 pp 26, 50): replace mac
* address of outgoing frame with that of the outgoing slave's.
*/
ether_addr_copy(hsr_ethhdr->ethhdr.h_source, skb->dev->dev_addr);
return dev_queue_xmit(skb);
return hsr_features_recompute(hsr, features);
}
static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct hsr_priv *hsr_priv;
struct hsr_ethhdr *hsr_ethhdr;
struct sk_buff *skb2;
int res1, res2;
struct hsr_priv *hsr = netdev_priv(dev);
struct hsr_port *master;
hsr_priv = netdev_priv(dev);
hsr_ethhdr = (struct hsr_ethhdr *) skb->data;
if ((skb->protocol != htons(ETH_P_PRP)) ||
(hsr_ethhdr->ethhdr.h_proto != htons(ETH_P_PRP))) {
hsr_fill_tag(hsr_ethhdr, hsr_priv);
skb->protocol = htons(ETH_P_PRP);
}
skb2 = pskb_copy(skb, GFP_ATOMIC);
res1 = NET_XMIT_DROP;
if (likely(hsr_priv->slave[HSR_DEV_SLAVE_A]))
res1 = slave_xmit(skb, hsr_priv, HSR_DEV_SLAVE_A);
res2 = NET_XMIT_DROP;
if (likely(skb2 && hsr_priv->slave[HSR_DEV_SLAVE_B]))
res2 = slave_xmit(skb2, hsr_priv, HSR_DEV_SLAVE_B);
if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN ||
res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) {
hsr_priv->dev->stats.tx_packets++;
hsr_priv->dev->stats.tx_bytes += skb->len;
} else {
hsr_priv->dev->stats.tx_dropped++;
}
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
skb->dev = master->dev;
hsr_forward_skb(skb, master);
return NETDEV_TX_OK;
}
static int hsr_header_create(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *daddr,
const void *saddr, unsigned int len)
{
int res;
/* Make room for the HSR tag now. We will fill it in later (in
* hsr_dev_xmit)
*/
if (skb_headroom(skb) < HSR_TAGLEN + ETH_HLEN)
return -ENOBUFS;
skb_push(skb, HSR_TAGLEN);
/* To allow VLAN/HSR combos we should probably use
* res = dev_hard_header(skb, dev, type, daddr, saddr, len + HSR_TAGLEN);
* here instead. It would require other changes too, though - e.g.
* separate headers for each slave etc...
*/
res = eth_header(skb, dev, type, daddr, saddr, len + HSR_TAGLEN);
if (res <= 0)
return res;
skb_reset_mac_header(skb);
return res + HSR_TAGLEN;
}
static const struct header_ops hsr_header_ops = {
.create = hsr_header_create,
.create = eth_header,
.parse = eth_header_parse,
};
@ -291,67 +256,63 @@ static const struct header_ops hsr_header_ops = {
*/
static int hsr_pad(int size)
{
const int min_size = ETH_ZLEN - HSR_TAGLEN - ETH_HLEN;
const int min_size = ETH_ZLEN - HSR_HLEN - ETH_HLEN;
if (size >= min_size)
return size;
return min_size;
}
static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type)
static void send_hsr_supervision_frame(struct hsr_port *master, u8 type)
{
struct hsr_priv *hsr_priv;
struct sk_buff *skb;
int hlen, tlen;
struct hsr_sup_tag *hsr_stag;
struct hsr_sup_payload *hsr_sp;
unsigned long irqflags;
hlen = LL_RESERVED_SPACE(hsr_dev);
tlen = hsr_dev->needed_tailroom;
hlen = LL_RESERVED_SPACE(master->dev);
tlen = master->dev->needed_tailroom;
skb = alloc_skb(hsr_pad(sizeof(struct hsr_sup_payload)) + hlen + tlen,
GFP_ATOMIC);
if (skb == NULL)
return;
hsr_priv = netdev_priv(hsr_dev);
skb_reserve(skb, hlen);
skb->dev = hsr_dev;
skb->dev = master->dev;
skb->protocol = htons(ETH_P_PRP);
skb->priority = TC_PRIO_CONTROL;
if (dev_hard_header(skb, skb->dev, ETH_P_PRP,
hsr_priv->sup_multicast_addr,
skb->dev->dev_addr, skb->len) < 0)
master->hsr->sup_multicast_addr,
skb->dev->dev_addr, skb->len) <= 0)
goto out;
skb_reset_mac_header(skb);
skb_pull(skb, sizeof(struct ethhdr));
hsr_stag = (typeof(hsr_stag)) skb->data;
hsr_stag = (typeof(hsr_stag)) skb_put(skb, sizeof(*hsr_stag));
set_hsr_stag_path(hsr_stag, 0xf);
set_hsr_stag_HSR_Ver(hsr_stag, 0);
spin_lock_irqsave(&hsr_priv->seqnr_lock, irqflags);
hsr_stag->sequence_nr = htons(hsr_priv->sequence_nr);
hsr_priv->sequence_nr++;
spin_unlock_irqrestore(&hsr_priv->seqnr_lock, irqflags);
spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags);
hsr_stag->sequence_nr = htons(master->hsr->sequence_nr);
master->hsr->sequence_nr++;
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
hsr_stag->HSR_TLV_Type = type;
hsr_stag->HSR_TLV_Length = 12;
skb_push(skb, sizeof(struct ethhdr));
/* Payload: MacAddressA */
hsr_sp = (typeof(hsr_sp)) skb_put(skb, sizeof(*hsr_sp));
ether_addr_copy(hsr_sp->MacAddressA, hsr_dev->dev_addr);
ether_addr_copy(hsr_sp->MacAddressA, master->dev->dev_addr);
dev_queue_xmit(skb);
hsr_forward_skb(skb, master);
return;
out:
WARN_ON_ONCE("HSR: Could not send supervision frame\n");
kfree_skb(skb);
}
@ -360,59 +321,32 @@ out:
*/
static void hsr_announce(unsigned long data)
{
struct hsr_priv *hsr_priv;
struct hsr_priv *hsr;
struct hsr_port *master;
hsr_priv = (struct hsr_priv *) data;
hsr = (struct hsr_priv *) data;
if (hsr_priv->announce_count < 3) {
send_hsr_supervision_frame(hsr_priv->dev, HSR_TLV_ANNOUNCE);
hsr_priv->announce_count++;
rcu_read_lock();
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
if (hsr->announce_count < 3) {
send_hsr_supervision_frame(master, HSR_TLV_ANNOUNCE);
hsr->announce_count++;
} else {
send_hsr_supervision_frame(hsr_priv->dev, HSR_TLV_LIFE_CHECK);
send_hsr_supervision_frame(master, HSR_TLV_LIFE_CHECK);
}
if (hsr_priv->announce_count < 3)
hsr_priv->announce_timer.expires = jiffies +
if (hsr->announce_count < 3)
hsr->announce_timer.expires = jiffies +
msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL);
else
hsr_priv->announce_timer.expires = jiffies +
hsr->announce_timer.expires = jiffies +
msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL);
if (is_admin_up(hsr_priv->dev))
add_timer(&hsr_priv->announce_timer);
}
if (is_admin_up(master->dev))
add_timer(&hsr->announce_timer);
static void restore_slaves(struct net_device *hsr_dev)
{
struct hsr_priv *hsr_priv;
int i;
int res;
hsr_priv = netdev_priv(hsr_dev);
rtnl_lock();
/* Restore promiscuity */
for (i = 0; i < HSR_MAX_SLAVE; i++) {
if (!hsr_priv->slave[i])
continue;
res = dev_set_promiscuity(hsr_priv->slave[i], -1);
if (res)
netdev_info(hsr_dev,
"Cannot restore slave promiscuity (%s, %d)\n",
hsr_priv->slave[i]->name, res);
}
rtnl_unlock();
}
static void reclaim_hsr_dev(struct rcu_head *rh)
{
struct hsr_priv *hsr_priv;
hsr_priv = container_of(rh, struct hsr_priv, rcu_head);
free_netdev(hsr_priv->dev);
rcu_read_unlock();
}
@ -421,14 +355,18 @@ static void reclaim_hsr_dev(struct rcu_head *rh)
*/
static void hsr_dev_destroy(struct net_device *hsr_dev)
{
struct hsr_priv *hsr_priv;
struct hsr_priv *hsr;
struct hsr_port *port;
hsr_priv = netdev_priv(hsr_dev);
hsr = netdev_priv(hsr_dev);
hsr_for_each_port(hsr, port)
hsr_del_port(port);
del_timer(&hsr_priv->announce_timer);
unregister_hsr_master(hsr_priv); /* calls list_del_rcu on hsr_priv */
restore_slaves(hsr_dev);
call_rcu(&hsr_priv->rcu_head, reclaim_hsr_dev); /* reclaim hsr_priv */
del_timer_sync(&hsr->prune_timer);
del_timer_sync(&hsr->announce_timer);
synchronize_rcu();
free_netdev(hsr_dev);
}
static const struct net_device_ops hsr_device_ops = {
@ -436,62 +374,51 @@ static const struct net_device_ops hsr_device_ops = {
.ndo_open = hsr_dev_open,
.ndo_stop = hsr_dev_close,
.ndo_start_xmit = hsr_dev_xmit,
.ndo_fix_features = hsr_fix_features,
};
static struct device_type hsr_type = {
.name = "hsr",
};
void hsr_dev_setup(struct net_device *dev)
{
random_ether_addr(dev->dev_addr);
ether_setup(dev);
dev->header_ops = &hsr_header_ops;
dev->netdev_ops = &hsr_device_ops;
dev->tx_queue_len = 0;
dev->header_ops = &hsr_header_ops;
dev->netdev_ops = &hsr_device_ops;
SET_NETDEV_DEVTYPE(dev, &hsr_type);
dev->tx_queue_len = 0;
dev->destructor = hsr_dev_destroy;
dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
NETIF_F_GSO_MASK | NETIF_F_HW_CSUM |
NETIF_F_HW_VLAN_CTAG_TX;
dev->features = dev->hw_features;
/* Prevent recursive tx locking */
dev->features |= NETIF_F_LLTX;
/* VLAN on top of HSR needs testing and probably some work on
* hsr_header_create() etc.
*/
dev->features |= NETIF_F_VLAN_CHALLENGED;
/* Not sure about this. Taken from bridge code. netdev_features.h says
* it means "Does not change network namespaces".
*/
dev->features |= NETIF_F_NETNS_LOCAL;
}
/* Return true if dev is a HSR master; return false otherwise.
*/
bool is_hsr_master(struct net_device *dev)
inline bool is_hsr_master(struct net_device *dev)
{
return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit);
}
static int check_slave_ok(struct net_device *dev)
{
/* Don't allow HSR on non-ethernet like devices */
if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) ||
(dev->addr_len != ETH_ALEN)) {
netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n");
return -EINVAL;
}
/* Don't allow enslaving hsr devices */
if (is_hsr_master(dev)) {
netdev_info(dev, "Cannot create trees of HSR devices.\n");
return -EINVAL;
}
if (is_hsr_slave(dev)) {
netdev_info(dev, "This device is already a HSR slave.\n");
return -EINVAL;
}
if (dev->priv_flags & IFF_802_1Q_VLAN) {
netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n");
return -EINVAL;
}
/* HSR over bonded devices has not been tested, but I'm not sure it
* won't work...
*/
return 0;
}
/* Default multicast address for HSR Supervision frames */
static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = {
0x01, 0x15, 0x4e, 0x00, 0x01, 0x00
@ -500,97 +427,74 @@ static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = {
int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
unsigned char multicast_spec)
{
struct hsr_priv *hsr_priv;
int i;
struct hsr_priv *hsr;
struct hsr_port *port;
int res;
hsr_priv = netdev_priv(hsr_dev);
hsr_priv->dev = hsr_dev;
INIT_LIST_HEAD(&hsr_priv->node_db);
INIT_LIST_HEAD(&hsr_priv->self_node_db);
for (i = 0; i < HSR_MAX_SLAVE; i++)
hsr_priv->slave[i] = slave[i];
hsr = netdev_priv(hsr_dev);
INIT_LIST_HEAD(&hsr->ports);
INIT_LIST_HEAD(&hsr->node_db);
INIT_LIST_HEAD(&hsr->self_node_db);
spin_lock_init(&hsr_priv->seqnr_lock);
ether_addr_copy(hsr_dev->dev_addr, slave[0]->dev_addr);
/* Make sure we recognize frames from ourselves in hsr_rcv() */
res = hsr_create_self_node(&hsr->self_node_db, hsr_dev->dev_addr,
slave[1]->dev_addr);
if (res < 0)
return res;
spin_lock_init(&hsr->seqnr_lock);
/* Overflow soon to find bugs easier: */
hsr_priv->sequence_nr = USHRT_MAX - 1024;
hsr->sequence_nr = HSR_SEQNR_START;
init_timer(&hsr_priv->announce_timer);
hsr_priv->announce_timer.function = hsr_announce;
hsr_priv->announce_timer.data = (unsigned long) hsr_priv;
init_timer(&hsr->announce_timer);
hsr->announce_timer.function = hsr_announce;
hsr->announce_timer.data = (unsigned long) hsr;
ether_addr_copy(hsr_priv->sup_multicast_addr, def_multicast_addr);
hsr_priv->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec;
init_timer(&hsr->prune_timer);
hsr->prune_timer.function = hsr_prune_nodes;
hsr->prune_timer.data = (unsigned long) hsr;
/* FIXME: should I modify the value of these?
*
* - hsr_dev->flags - i.e.
* IFF_MASTER/SLAVE?
* - hsr_dev->priv_flags - i.e.
* IFF_EBRIDGE?
* IFF_TX_SKB_SHARING?
* IFF_HSR_MASTER/SLAVE?
*/
ether_addr_copy(hsr->sup_multicast_addr, def_multicast_addr);
hsr->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec;
for (i = 0; i < HSR_MAX_SLAVE; i++) {
res = check_slave_ok(slave[i]);
if (res)
return res;
}
hsr_dev->features = slave[0]->features & slave[1]->features;
/* Prevent recursive tx locking */
hsr_dev->features |= NETIF_F_LLTX;
/* VLAN on top of HSR needs testing and probably some work on
* hsr_header_create() etc.
/* FIXME: should I modify the value of these?
*
* - hsr_dev->flags - i.e.
* IFF_MASTER/SLAVE?
* - hsr_dev->priv_flags - i.e.
* IFF_EBRIDGE?
* IFF_TX_SKB_SHARING?
* IFF_HSR_MASTER/SLAVE?
*/
hsr_dev->features |= NETIF_F_VLAN_CHALLENGED;
/* Set hsr_dev's MAC address to that of mac_slave1 */
ether_addr_copy(hsr_dev->dev_addr, hsr_priv->slave[0]->dev_addr);
/* Set required header length */
for (i = 0; i < HSR_MAX_SLAVE; i++) {
if (slave[i]->hard_header_len + HSR_TAGLEN >
hsr_dev->hard_header_len)
hsr_dev->hard_header_len =
slave[i]->hard_header_len + HSR_TAGLEN;
}
/* MTU */
for (i = 0; i < HSR_MAX_SLAVE; i++)
if (slave[i]->mtu - HSR_TAGLEN < hsr_dev->mtu)
hsr_dev->mtu = slave[i]->mtu - HSR_TAGLEN;
/* Make sure the 1st call to netif_carrier_on() gets through */
netif_carrier_off(hsr_dev);
/* Promiscuity */
for (i = 0; i < HSR_MAX_SLAVE; i++) {
res = dev_set_promiscuity(slave[i], 1);
if (res) {
netdev_info(hsr_dev, "Cannot set slave promiscuity (%s, %d)\n",
slave[i]->name, res);
goto fail;
}
}
/* Make sure we recognize frames from ourselves in hsr_rcv() */
res = hsr_create_self_node(&hsr_priv->self_node_db,
hsr_dev->dev_addr,
hsr_priv->slave[1]->dev_addr);
if (res < 0)
goto fail;
res = hsr_add_port(hsr, hsr_dev, HSR_PT_MASTER);
if (res)
return res;
res = register_netdevice(hsr_dev);
if (res)
goto fail;
register_hsr_master(hsr_priv);
res = hsr_add_port(hsr, slave[0], HSR_PT_SLAVE_A);
if (res)
goto fail;
res = hsr_add_port(hsr, slave[1], HSR_PT_SLAVE_B);
if (res)
goto fail;
hsr->prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD);
add_timer(&hsr->prune_timer);
return 0;
fail:
restore_slaves(hsr_dev);
hsr_for_each_port(hsr, port)
hsr_del_port(port);
return res;
}

View file

@ -1,4 +1,4 @@
/* Copyright 2011-2013 Autronica Fire and Security AS
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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
@ -6,7 +6,7 @@
* any later version.
*
* Author(s):
* 2011-2013 Arvid Brodin, arvid.brodin@xdin.com
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#ifndef __HSR_DEVICE_H
@ -18,12 +18,8 @@
void hsr_dev_setup(struct net_device *dev);
int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
unsigned char multicast_spec);
void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1,
struct net_device *slave2);
void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1,
struct net_device *slave2);
void hsr_check_announce(struct net_device *hsr_dev, int old_operstate);
void hsr_check_carrier_and_operstate(struct hsr_priv *hsr);
bool is_hsr_master(struct net_device *dev);
int hsr_get_max_mtu(struct hsr_priv *hsr_priv);
int hsr_get_max_mtu(struct hsr_priv *hsr);
#endif /* __HSR_DEVICE_H */

368
net/hsr/hsr_forward.c Normal file
View file

@ -0,0 +1,368 @@
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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.
*
* Author(s):
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#include "hsr_forward.h"
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include "hsr_main.h"
#include "hsr_framereg.h"
struct hsr_node;
struct hsr_frame_info {
struct sk_buff *skb_std;
struct sk_buff *skb_hsr;
struct hsr_port *port_rcv;
struct hsr_node *node_src;
u16 sequence_nr;
bool is_supervision;
bool is_vlan;
bool is_local_dest;
bool is_local_exclusive;
};
/* The uses I can see for these HSR supervision frames are:
* 1) Use the frames that are sent after node initialization ("HSR_TLV.Type =
* 22") to reset any sequence_nr counters belonging to that node. Useful if
* the other node's counter has been reset for some reason.
* --
* Or not - resetting the counter and bridging the frame would create a
* loop, unfortunately.
*
* 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck
* frame is received from a particular node, we know something is wrong.
* We just register these (as with normal frames) and throw them away.
*
* 3) Allow different MAC addresses for the two slave interfaces, using the
* MacAddressA field.
*/
static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
{
struct hsr_ethhdr_sp *hdr;
WARN_ON_ONCE(!skb_mac_header_was_set(skb));
hdr = (struct hsr_ethhdr_sp *) skb_mac_header(skb);
if (!ether_addr_equal(hdr->ethhdr.h_dest,
hsr->sup_multicast_addr))
return false;
if (get_hsr_stag_path(&hdr->hsr_sup) != 0x0f)
return false;
if ((hdr->hsr_sup.HSR_TLV_Type != HSR_TLV_ANNOUNCE) &&
(hdr->hsr_sup.HSR_TLV_Type != HSR_TLV_LIFE_CHECK))
return false;
if (hdr->hsr_sup.HSR_TLV_Length != 12)
return false;
return true;
}
static struct sk_buff *create_stripped_skb(struct sk_buff *skb_in,
struct hsr_frame_info *frame)
{
struct sk_buff *skb;
int copylen;
unsigned char *dst, *src;
skb_pull(skb_in, HSR_HLEN);
skb = __pskb_copy(skb_in, skb_headroom(skb_in) - HSR_HLEN, GFP_ATOMIC);
skb_push(skb_in, HSR_HLEN);
if (skb == NULL)
return NULL;
skb_reset_mac_header(skb);
if (skb->ip_summed == CHECKSUM_PARTIAL)
skb->csum_start -= HSR_HLEN;
copylen = 2*ETH_ALEN;
if (frame->is_vlan)
copylen += VLAN_HLEN;
src = skb_mac_header(skb_in);
dst = skb_mac_header(skb);
memcpy(dst, src, copylen);
skb->protocol = eth_hdr(skb)->h_proto;
return skb;
}
static struct sk_buff *frame_get_stripped_skb(struct hsr_frame_info *frame,
struct hsr_port *port)
{
if (!frame->skb_std)
frame->skb_std = create_stripped_skb(frame->skb_hsr, frame);
return skb_clone(frame->skb_std, GFP_ATOMIC);
}
static void hsr_fill_tag(struct sk_buff *skb, struct hsr_frame_info *frame,
struct hsr_port *port)
{
struct hsr_ethhdr *hsr_ethhdr;
int lane_id;
int lsdu_size;
if (port->type == HSR_PT_SLAVE_A)
lane_id = 0;
else
lane_id = 1;
lsdu_size = skb->len - 14;
if (frame->is_vlan)
lsdu_size -= 4;
hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb);
set_hsr_tag_path(&hsr_ethhdr->hsr_tag, lane_id);
set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, lsdu_size);
hsr_ethhdr->hsr_tag.sequence_nr = htons(frame->sequence_nr);
hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto;
hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_PRP);
}
static struct sk_buff *create_tagged_skb(struct sk_buff *skb_o,
struct hsr_frame_info *frame,
struct hsr_port *port)
{
int movelen;
unsigned char *dst, *src;
struct sk_buff *skb;
/* Create the new skb with enough headroom to fit the HSR tag */
skb = __pskb_copy(skb_o, skb_headroom(skb_o) + HSR_HLEN, GFP_ATOMIC);
if (skb == NULL)
return NULL;
skb_reset_mac_header(skb);
if (skb->ip_summed == CHECKSUM_PARTIAL)
skb->csum_start += HSR_HLEN;
movelen = ETH_HLEN;
if (frame->is_vlan)
movelen += VLAN_HLEN;
src = skb_mac_header(skb);
dst = skb_push(skb, HSR_HLEN);
memmove(dst, src, movelen);
skb_reset_mac_header(skb);
hsr_fill_tag(skb, frame, port);
return skb;
}
/* If the original frame was an HSR tagged frame, just clone it to be sent
* unchanged. Otherwise, create a private frame especially tagged for 'port'.
*/
static struct sk_buff *frame_get_tagged_skb(struct hsr_frame_info *frame,
struct hsr_port *port)
{
if (frame->skb_hsr)
return skb_clone(frame->skb_hsr, GFP_ATOMIC);
if ((port->type != HSR_PT_SLAVE_A) && (port->type != HSR_PT_SLAVE_B)) {
WARN_ONCE(1, "HSR: Bug: trying to create a tagged frame for a non-ring port");
return NULL;
}
return create_tagged_skb(frame->skb_std, frame, port);
}
static void hsr_deliver_master(struct sk_buff *skb, struct net_device *dev,
struct hsr_node *node_src)
{
bool was_multicast_frame;
int res;
was_multicast_frame = (skb->pkt_type == PACKET_MULTICAST);
hsr_addr_subst_source(node_src, skb);
skb_pull(skb, ETH_HLEN);
res = netif_rx(skb);
if (res == NET_RX_DROP) {
dev->stats.rx_dropped++;
} else {
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
if (was_multicast_frame)
dev->stats.multicast++;
}
}
static int hsr_xmit(struct sk_buff *skb, struct hsr_port *port,
struct hsr_frame_info *frame)
{
if (frame->port_rcv->type == HSR_PT_MASTER) {
hsr_addr_subst_dest(frame->node_src, skb, port);
/* Address substitution (IEC62439-3 pp 26, 50): replace mac
* address of outgoing frame with that of the outgoing slave's.
*/
ether_addr_copy(eth_hdr(skb)->h_source, port->dev->dev_addr);
}
return dev_queue_xmit(skb);
}
/* Forward the frame through all devices except:
* - Back through the receiving device
* - If it's a HSR frame: through a device where it has passed before
* - To the local HSR master only if the frame is directly addressed to it, or
* a non-supervision multicast or broadcast frame.
*
* HSR slave devices should insert a HSR tag into the frame, or forward the
* frame unchanged if it's already tagged. Interlink devices should strip HSR
* tags if they're of the non-HSR type (but only after duplicate discard). The
* master device always strips HSR tags.
*/
static void hsr_forward_do(struct hsr_frame_info *frame)
{
struct hsr_port *port;
struct sk_buff *skb;
hsr_for_each_port(frame->port_rcv->hsr, port) {
/* Don't send frame back the way it came */
if (port == frame->port_rcv)
continue;
/* Don't deliver locally unless we should */
if ((port->type == HSR_PT_MASTER) && !frame->is_local_dest)
continue;
/* Deliver frames directly addressed to us to master only */
if ((port->type != HSR_PT_MASTER) && frame->is_local_exclusive)
continue;
/* Don't send frame over port where it has been sent before */
if (hsr_register_frame_out(port, frame->node_src,
frame->sequence_nr))
continue;
if (frame->is_supervision && (port->type == HSR_PT_MASTER)) {
hsr_handle_sup_frame(frame->skb_hsr,
frame->node_src,
frame->port_rcv);
continue;
}
if (port->type != HSR_PT_MASTER)
skb = frame_get_tagged_skb(frame, port);
else
skb = frame_get_stripped_skb(frame, port);
if (skb == NULL) {
/* FIXME: Record the dropped frame? */
continue;
}
skb->dev = port->dev;
if (port->type == HSR_PT_MASTER)
hsr_deliver_master(skb, port->dev, frame->node_src);
else
hsr_xmit(skb, port, frame);
}
}
static void check_local_dest(struct hsr_priv *hsr, struct sk_buff *skb,
struct hsr_frame_info *frame)
{
struct net_device *master_dev;
master_dev = hsr_port_get_hsr(hsr, HSR_PT_MASTER)->dev;
if (hsr_addr_is_self(hsr, eth_hdr(skb)->h_dest)) {
frame->is_local_exclusive = true;
skb->pkt_type = PACKET_HOST;
} else {
frame->is_local_exclusive = false;
}
if ((skb->pkt_type == PACKET_HOST) ||
(skb->pkt_type == PACKET_MULTICAST) ||
(skb->pkt_type == PACKET_BROADCAST)) {
frame->is_local_dest = true;
} else {
frame->is_local_dest = false;
}
}
static int hsr_fill_frame_info(struct hsr_frame_info *frame,
struct sk_buff *skb, struct hsr_port *port)
{
struct ethhdr *ethhdr;
unsigned long irqflags;
frame->is_supervision = is_supervision_frame(port->hsr, skb);
frame->node_src = hsr_get_node(&port->hsr->node_db, skb,
frame->is_supervision);
if (frame->node_src == NULL)
return -1; /* Unknown node and !is_supervision, or no mem */
ethhdr = (struct ethhdr *) skb_mac_header(skb);
frame->is_vlan = false;
if (ethhdr->h_proto == htons(ETH_P_8021Q)) {
frame->is_vlan = true;
/* FIXME: */
WARN_ONCE(1, "HSR: VLAN not yet supported");
}
if (ethhdr->h_proto == htons(ETH_P_PRP)) {
frame->skb_std = NULL;
frame->skb_hsr = skb;
frame->sequence_nr = hsr_get_skb_sequence_nr(skb);
} else {
frame->skb_std = skb;
frame->skb_hsr = NULL;
/* Sequence nr for the master node */
spin_lock_irqsave(&port->hsr->seqnr_lock, irqflags);
frame->sequence_nr = port->hsr->sequence_nr;
port->hsr->sequence_nr++;
spin_unlock_irqrestore(&port->hsr->seqnr_lock, irqflags);
}
frame->port_rcv = port;
check_local_dest(port->hsr, skb, frame);
return 0;
}
/* Must be called holding rcu read lock (because of the port parameter) */
void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
{
struct hsr_frame_info frame;
if (skb_mac_header(skb) != skb->data) {
WARN_ONCE(1, "%s:%d: Malformed frame (port_src %s)\n",
__FILE__, __LINE__, port->dev->name);
goto out_drop;
}
if (hsr_fill_frame_info(&frame, skb, port) < 0)
goto out_drop;
hsr_register_frame_in(frame.node_src, port, frame.sequence_nr);
hsr_forward_do(&frame);
if (frame.skb_hsr != NULL)
kfree_skb(frame.skb_hsr);
if (frame.skb_std != NULL)
kfree_skb(frame.skb_std);
return;
out_drop:
port->dev->stats.tx_dropped++;
kfree_skb(skb);
}

20
net/hsr/hsr_forward.h Normal file
View file

@ -0,0 +1,20 @@
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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.
*
* Author(s):
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#ifndef __HSR_FORWARD_H
#define __HSR_FORWARD_H
#include <linux/netdevice.h>
#include "hsr_main.h"
void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port);
#endif /* __HSR_FORWARD_H */

View file

@ -1,4 +1,4 @@
/* Copyright 2011-2013 Autronica Fire and Security AS
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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
@ -6,7 +6,7 @@
* any later version.
*
* Author(s):
* 2011-2013 Arvid Brodin, arvid.brodin@xdin.com
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*
* The HSR spec says never to forward the same frame twice on the same
* interface. A frame is identified by its source MAC address and its HSR
@ -23,260 +23,22 @@
#include "hsr_netlink.h"
struct node_entry {
struct list_head mac_list;
unsigned char MacAddressA[ETH_ALEN];
unsigned char MacAddressB[ETH_ALEN];
enum hsr_dev_idx AddrB_if; /* The local slave through which AddrB
* frames are received from this node
*/
unsigned long time_in[HSR_MAX_SLAVE];
bool time_in_stale[HSR_MAX_SLAVE];
u16 seq_out[HSR_MAX_DEV];
struct rcu_head rcu_head;
struct hsr_node {
struct list_head mac_list;
unsigned char MacAddressA[ETH_ALEN];
unsigned char MacAddressB[ETH_ALEN];
/* Local slave through which AddrB frames are received from this node */
enum hsr_port_type AddrB_port;
unsigned long time_in[HSR_PT_PORTS];
bool time_in_stale[HSR_PT_PORTS];
u16 seq_out[HSR_PT_PORTS];
struct rcu_head rcu_head;
};
/* TODO: use hash lists for mac addresses (linux/jhash.h)? */
/* Search for mac entry. Caller must hold rcu read lock.
*/
static struct node_entry *find_node_by_AddrA(struct list_head *node_db,
const unsigned char addr[ETH_ALEN])
{
struct node_entry *node;
list_for_each_entry_rcu(node, node_db, mac_list) {
if (ether_addr_equal(node->MacAddressA, addr))
return node;
}
return NULL;
}
/* Search for mac entry. Caller must hold rcu read lock.
*/
static struct node_entry *find_node_by_AddrB(struct list_head *node_db,
const unsigned char addr[ETH_ALEN])
{
struct node_entry *node;
list_for_each_entry_rcu(node, node_db, mac_list) {
if (ether_addr_equal(node->MacAddressB, addr))
return node;
}
return NULL;
}
/* Search for mac entry. Caller must hold rcu read lock.
*/
struct node_entry *hsr_find_node(struct list_head *node_db, struct sk_buff *skb)
{
struct node_entry *node;
struct ethhdr *ethhdr;
if (!skb_mac_header_was_set(skb))
return NULL;
ethhdr = (struct ethhdr *) skb_mac_header(skb);
list_for_each_entry_rcu(node, node_db, mac_list) {
if (ether_addr_equal(node->MacAddressA, ethhdr->h_source))
return node;
if (ether_addr_equal(node->MacAddressB, ethhdr->h_source))
return node;
}
return NULL;
}
/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize
* frames from self that's been looped over the HSR ring.
*/
int hsr_create_self_node(struct list_head *self_node_db,
unsigned char addr_a[ETH_ALEN],
unsigned char addr_b[ETH_ALEN])
{
struct node_entry *node, *oldnode;
node = kmalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return -ENOMEM;
ether_addr_copy(node->MacAddressA, addr_a);
ether_addr_copy(node->MacAddressB, addr_b);
rcu_read_lock();
oldnode = list_first_or_null_rcu(self_node_db,
struct node_entry, mac_list);
if (oldnode) {
list_replace_rcu(&oldnode->mac_list, &node->mac_list);
rcu_read_unlock();
synchronize_rcu();
kfree(oldnode);
} else {
rcu_read_unlock();
list_add_tail_rcu(&node->mac_list, self_node_db);
}
return 0;
}
/* Add/merge node to the database of nodes. 'skb' must contain an HSR
* supervision frame.
* - If the supervision header's MacAddressA field is not yet in the database,
* this frame is from an hitherto unknown node - add it to the database.
* - If the sender's MAC address is not the same as its MacAddressA address,
* the node is using PICS_SUBS (address substitution). Record the sender's
* address as the node's MacAddressB.
*
* This function needs to work even if the sender node has changed one of its
* slaves' MAC addresses. In this case, there are four different cases described
* by (Addr-changed, received-from) pairs as follows. Note that changing the
* SlaveA address is equal to changing the node's own address:
*
* - (AddrB, SlaveB): The new AddrB will be recorded by PICS_SUBS code since
* node == NULL.
* - (AddrB, SlaveA): Will work as usual (the AddrB change won't be detected
* from this frame).
*
* - (AddrA, SlaveB): The old node will be found. We need to detect this and
* remove the node.
* - (AddrA, SlaveA): A new node will be registered (non-PICS_SUBS at first).
* The old one will be pruned after HSR_NODE_FORGET_TIME.
*
* We also need to detect if the sender's SlaveA and SlaveB cables have been
* swapped.
*/
struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
struct node_entry *node,
struct sk_buff *skb,
enum hsr_dev_idx dev_idx)
{
struct hsr_sup_payload *hsr_sp;
struct hsr_ethhdr_sp *hsr_ethsup;
int i;
unsigned long now;
hsr_ethsup = (struct hsr_ethhdr_sp *) skb_mac_header(skb);
hsr_sp = (struct hsr_sup_payload *) skb->data;
if (node && !ether_addr_equal(node->MacAddressA, hsr_sp->MacAddressA)) {
/* Node has changed its AddrA, frame was received from SlaveB */
list_del_rcu(&node->mac_list);
kfree_rcu(node, rcu_head);
node = NULL;
}
if (node && (dev_idx == node->AddrB_if) &&
!ether_addr_equal(node->MacAddressB, hsr_ethsup->ethhdr.h_source)) {
/* Cables have been swapped */
list_del_rcu(&node->mac_list);
kfree_rcu(node, rcu_head);
node = NULL;
}
if (node && (dev_idx != node->AddrB_if) &&
(node->AddrB_if != HSR_DEV_NONE) &&
!ether_addr_equal(node->MacAddressA, hsr_ethsup->ethhdr.h_source)) {
/* Cables have been swapped */
list_del_rcu(&node->mac_list);
kfree_rcu(node, rcu_head);
node = NULL;
}
if (node)
return node;
node = find_node_by_AddrA(&hsr_priv->node_db, hsr_sp->MacAddressA);
if (node) {
/* Node is known, but frame was received from an unknown
* address. Node is PICS_SUBS capable; merge its AddrB.
*/
ether_addr_copy(node->MacAddressB, hsr_ethsup->ethhdr.h_source);
node->AddrB_if = dev_idx;
return node;
}
node = kzalloc(sizeof(*node), GFP_ATOMIC);
if (!node)
return NULL;
ether_addr_copy(node->MacAddressA, hsr_sp->MacAddressA);
ether_addr_copy(node->MacAddressB, hsr_ethsup->ethhdr.h_source);
if (!ether_addr_equal(hsr_sp->MacAddressA, hsr_ethsup->ethhdr.h_source))
node->AddrB_if = dev_idx;
else
node->AddrB_if = HSR_DEV_NONE;
/* We are only interested in time diffs here, so use current jiffies
* as initialization. (0 could trigger an spurious ring error warning).
*/
now = jiffies;
for (i = 0; i < HSR_MAX_SLAVE; i++)
node->time_in[i] = now;
for (i = 0; i < HSR_MAX_DEV; i++)
node->seq_out[i] = ntohs(hsr_ethsup->hsr_sup.sequence_nr) - 1;
list_add_tail_rcu(&node->mac_list, &hsr_priv->node_db);
return node;
}
/* 'skb' is a frame meant for this host, that is to be passed to upper layers.
*
* If the frame was sent by a node's B interface, replace the sender
* address with that node's "official" address (MacAddressA) so that upper
* layers recognize where it came from.
*/
void hsr_addr_subst_source(struct hsr_priv *hsr_priv, struct sk_buff *skb)
{
struct ethhdr *ethhdr;
struct node_entry *node;
if (!skb_mac_header_was_set(skb)) {
WARN_ONCE(1, "%s: Mac header not set\n", __func__);
return;
}
ethhdr = (struct ethhdr *) skb_mac_header(skb);
rcu_read_lock();
node = find_node_by_AddrB(&hsr_priv->node_db, ethhdr->h_source);
if (node)
ether_addr_copy(ethhdr->h_source, node->MacAddressA);
rcu_read_unlock();
}
/* 'skb' is a frame meant for another host.
* 'hsr_dev_idx' is the HSR index of the outgoing device
*
* Substitute the target (dest) MAC address if necessary, so the it matches the
* recipient interface MAC address, regardless of whether that is the
* recipient's A or B interface.
* This is needed to keep the packets flowing through switches that learn on
* which "side" the different interfaces are.
*/
void hsr_addr_subst_dest(struct hsr_priv *hsr_priv, struct ethhdr *ethhdr,
enum hsr_dev_idx dev_idx)
{
struct node_entry *node;
rcu_read_lock();
node = find_node_by_AddrA(&hsr_priv->node_db, ethhdr->h_dest);
if (node && (node->AddrB_if == dev_idx))
ether_addr_copy(ethhdr->h_dest, node->MacAddressB);
rcu_read_unlock();
}
/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b,
* false otherwise.
*/
@ -295,16 +57,257 @@ static bool seq_nr_after(u16 a, u16 b)
#define seq_nr_before_or_eq(a, b) (!seq_nr_after((a), (b)))
void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx dev_idx)
bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr)
{
if ((dev_idx < 0) || (dev_idx >= HSR_MAX_SLAVE)) {
WARN_ONCE(1, "%s: Invalid dev_idx (%d)\n", __func__, dev_idx);
return;
struct hsr_node *node;
node = list_first_or_null_rcu(&hsr->self_node_db, struct hsr_node,
mac_list);
if (!node) {
WARN_ONCE(1, "HSR: No self node\n");
return false;
}
node->time_in[dev_idx] = jiffies;
node->time_in_stale[dev_idx] = false;
if (ether_addr_equal(addr, node->MacAddressA))
return true;
if (ether_addr_equal(addr, node->MacAddressB))
return true;
return false;
}
/* Search for mac entry. Caller must hold rcu read lock.
*/
static struct hsr_node *find_node_by_AddrA(struct list_head *node_db,
const unsigned char addr[ETH_ALEN])
{
struct hsr_node *node;
list_for_each_entry_rcu(node, node_db, mac_list) {
if (ether_addr_equal(node->MacAddressA, addr))
return node;
}
return NULL;
}
/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize
* frames from self that's been looped over the HSR ring.
*/
int hsr_create_self_node(struct list_head *self_node_db,
unsigned char addr_a[ETH_ALEN],
unsigned char addr_b[ETH_ALEN])
{
struct hsr_node *node, *oldnode;
node = kmalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return -ENOMEM;
ether_addr_copy(node->MacAddressA, addr_a);
ether_addr_copy(node->MacAddressB, addr_b);
rcu_read_lock();
oldnode = list_first_or_null_rcu(self_node_db,
struct hsr_node, mac_list);
if (oldnode) {
list_replace_rcu(&oldnode->mac_list, &node->mac_list);
rcu_read_unlock();
synchronize_rcu();
kfree(oldnode);
} else {
rcu_read_unlock();
list_add_tail_rcu(&node->mac_list, self_node_db);
}
return 0;
}
/* Allocate an hsr_node and add it to node_db. 'addr' is the node's AddressA;
* seq_out is used to initialize filtering of outgoing duplicate frames
* originating from the newly added node.
*/
struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[],
u16 seq_out)
{
struct hsr_node *node;
unsigned long now;
int i;
node = kzalloc(sizeof(*node), GFP_ATOMIC);
if (!node)
return NULL;
ether_addr_copy(node->MacAddressA, addr);
/* We are only interested in time diffs here, so use current jiffies
* as initialization. (0 could trigger an spurious ring error warning).
*/
now = jiffies;
for (i = 0; i < HSR_PT_PORTS; i++)
node->time_in[i] = now;
for (i = 0; i < HSR_PT_PORTS; i++)
node->seq_out[i] = seq_out;
list_add_tail_rcu(&node->mac_list, node_db);
return node;
}
/* Get the hsr_node from which 'skb' was sent.
*/
struct hsr_node *hsr_get_node(struct list_head *node_db, struct sk_buff *skb,
bool is_sup)
{
struct hsr_node *node;
struct ethhdr *ethhdr;
u16 seq_out;
if (!skb_mac_header_was_set(skb))
return NULL;
ethhdr = (struct ethhdr *) skb_mac_header(skb);
list_for_each_entry_rcu(node, node_db, mac_list) {
if (ether_addr_equal(node->MacAddressA, ethhdr->h_source))
return node;
if (ether_addr_equal(node->MacAddressB, ethhdr->h_source))
return node;
}
if (!is_sup)
return NULL; /* Only supervision frame may create node entry */
if (ethhdr->h_proto == htons(ETH_P_PRP)) {
/* Use the existing sequence_nr from the tag as starting point
* for filtering duplicate frames.
*/
seq_out = hsr_get_skb_sequence_nr(skb) - 1;
} else {
WARN_ONCE(1, "%s: Non-HSR frame\n", __func__);
seq_out = 0;
}
return hsr_add_node(node_db, ethhdr->h_source, seq_out);
}
/* Use the Supervision frame's info about an eventual MacAddressB for merging
* nodes that has previously had their MacAddressB registered as a separate
* node.
*/
void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
struct hsr_port *port_rcv)
{
struct hsr_node *node_real;
struct hsr_sup_payload *hsr_sp;
struct list_head *node_db;
int i;
skb_pull(skb, sizeof(struct hsr_ethhdr_sp));
hsr_sp = (struct hsr_sup_payload *) skb->data;
if (ether_addr_equal(eth_hdr(skb)->h_source, hsr_sp->MacAddressA))
/* Not sent from MacAddressB of a PICS_SUBS capable node */
goto done;
/* Merge node_curr (registered on MacAddressB) into node_real */
node_db = &port_rcv->hsr->node_db;
node_real = find_node_by_AddrA(node_db, hsr_sp->MacAddressA);
if (!node_real)
/* No frame received from AddrA of this node yet */
node_real = hsr_add_node(node_db, hsr_sp->MacAddressA,
HSR_SEQNR_START - 1);
if (!node_real)
goto done; /* No mem */
if (node_real == node_curr)
/* Node has already been merged */
goto done;
ether_addr_copy(node_real->MacAddressB, eth_hdr(skb)->h_source);
for (i = 0; i < HSR_PT_PORTS; i++) {
if (!node_curr->time_in_stale[i] &&
time_after(node_curr->time_in[i], node_real->time_in[i])) {
node_real->time_in[i] = node_curr->time_in[i];
node_real->time_in_stale[i] = node_curr->time_in_stale[i];
}
if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i]))
node_real->seq_out[i] = node_curr->seq_out[i];
}
node_real->AddrB_port = port_rcv->type;
list_del_rcu(&node_curr->mac_list);
kfree_rcu(node_curr, rcu_head);
done:
skb_push(skb, sizeof(struct hsr_ethhdr_sp));
}
/* 'skb' is a frame meant for this host, that is to be passed to upper layers.
*
* If the frame was sent by a node's B interface, replace the source
* address with that node's "official" address (MacAddressA) so that upper
* layers recognize where it came from.
*/
void hsr_addr_subst_source(struct hsr_node *node, struct sk_buff *skb)
{
if (!skb_mac_header_was_set(skb)) {
WARN_ONCE(1, "%s: Mac header not set\n", __func__);
return;
}
memcpy(&eth_hdr(skb)->h_source, node->MacAddressA, ETH_ALEN);
}
/* 'skb' is a frame meant for another host.
* 'port' is the outgoing interface
*
* Substitute the target (dest) MAC address if necessary, so the it matches the
* recipient interface MAC address, regardless of whether that is the
* recipient's A or B interface.
* This is needed to keep the packets flowing through switches that learn on
* which "side" the different interfaces are.
*/
void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
struct hsr_port *port)
{
struct hsr_node *node_dst;
if (!skb_mac_header_was_set(skb)) {
WARN_ONCE(1, "%s: Mac header not set\n", __func__);
return;
}
if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest))
return;
node_dst = find_node_by_AddrA(&port->hsr->node_db, eth_hdr(skb)->h_dest);
if (!node_dst) {
WARN_ONCE(1, "%s: Unknown node\n", __func__);
return;
}
if (port->type != node_dst->AddrB_port)
return;
ether_addr_copy(eth_hdr(skb)->h_dest, node_dst->MacAddressB);
}
void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port,
u16 sequence_nr)
{
/* Don't register incoming frames without a valid sequence number. This
* ensures entries of restarted nodes gets pruned so that they can
* re-register and resume communications.
*/
if (seq_nr_before(sequence_nr, node->seq_out[port->type]))
return;
node->time_in[port->type] = jiffies;
node->time_in_stale[port->type] = false;
}
/* 'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid
* ethhdr->h_source address and skb->mac_header set.
@ -314,102 +317,87 @@ void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx dev_idx)
* 0 otherwise, or
* negative error code on error
*/
int hsr_register_frame_out(struct node_entry *node, enum hsr_dev_idx dev_idx,
struct sk_buff *skb)
int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node,
u16 sequence_nr)
{
struct hsr_ethhdr *hsr_ethhdr;
u16 sequence_nr;
if ((dev_idx < 0) || (dev_idx >= HSR_MAX_DEV)) {
WARN_ONCE(1, "%s: Invalid dev_idx (%d)\n", __func__, dev_idx);
return -EINVAL;
}
if (!skb_mac_header_was_set(skb)) {
WARN_ONCE(1, "%s: Mac header not set\n", __func__);
return -EINVAL;
}
hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb);
sequence_nr = ntohs(hsr_ethhdr->hsr_tag.sequence_nr);
if (seq_nr_before_or_eq(sequence_nr, node->seq_out[dev_idx]))
if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]))
return 1;
node->seq_out[dev_idx] = sequence_nr;
node->seq_out[port->type] = sequence_nr;
return 0;
}
static bool is_late(struct node_entry *node, enum hsr_dev_idx dev_idx)
static struct hsr_port *get_late_port(struct hsr_priv *hsr,
struct hsr_node *node)
{
enum hsr_dev_idx other;
if (node->time_in_stale[HSR_PT_SLAVE_A])
return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
if (node->time_in_stale[HSR_PT_SLAVE_B])
return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
if (node->time_in_stale[dev_idx])
return true;
if (time_after(node->time_in[HSR_PT_SLAVE_B],
node->time_in[HSR_PT_SLAVE_A] +
msecs_to_jiffies(MAX_SLAVE_DIFF)))
return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
if (time_after(node->time_in[HSR_PT_SLAVE_A],
node->time_in[HSR_PT_SLAVE_B] +
msecs_to_jiffies(MAX_SLAVE_DIFF)))
return hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
if (dev_idx == HSR_DEV_SLAVE_A)
other = HSR_DEV_SLAVE_B;
else
other = HSR_DEV_SLAVE_A;
if (node->time_in_stale[other])
return false;
if (time_after(node->time_in[other], node->time_in[dev_idx] +
msecs_to_jiffies(MAX_SLAVE_DIFF)))
return true;
return false;
return NULL;
}
/* Remove stale sequence_nr records. Called by timer every
* HSR_LIFE_CHECK_INTERVAL (two seconds or so).
*/
void hsr_prune_nodes(struct hsr_priv *hsr_priv)
void hsr_prune_nodes(unsigned long data)
{
struct node_entry *node;
struct hsr_priv *hsr;
struct hsr_node *node;
struct hsr_port *port;
unsigned long timestamp;
unsigned long time_a, time_b;
hsr = (struct hsr_priv *) data;
rcu_read_lock();
list_for_each_entry_rcu(node, &hsr_priv->node_db, mac_list) {
list_for_each_entry_rcu(node, &hsr->node_db, mac_list) {
/* Shorthand */
time_a = node->time_in[HSR_DEV_SLAVE_A];
time_b = node->time_in[HSR_DEV_SLAVE_B];
time_a = node->time_in[HSR_PT_SLAVE_A];
time_b = node->time_in[HSR_PT_SLAVE_B];
/* Check for timestamps old enough to risk wrap-around */
if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET/2))
node->time_in_stale[HSR_DEV_SLAVE_A] = true;
node->time_in_stale[HSR_PT_SLAVE_A] = true;
if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET/2))
node->time_in_stale[HSR_DEV_SLAVE_B] = true;
node->time_in_stale[HSR_PT_SLAVE_B] = true;
/* Get age of newest frame from node.
* At least one time_in is OK here; nodes get pruned long
* before both time_ins can get stale
*/
timestamp = time_a;
if (node->time_in_stale[HSR_DEV_SLAVE_A] ||
(!node->time_in_stale[HSR_DEV_SLAVE_B] &&
if (node->time_in_stale[HSR_PT_SLAVE_A] ||
(!node->time_in_stale[HSR_PT_SLAVE_B] &&
time_after(time_b, time_a)))
timestamp = time_b;
/* Warn of ring error only as long as we get frames at all */
if (time_is_after_jiffies(timestamp +
msecs_to_jiffies(1.5*MAX_SLAVE_DIFF))) {
if (is_late(node, HSR_DEV_SLAVE_A))
hsr_nl_ringerror(hsr_priv, node->MacAddressA,
HSR_DEV_SLAVE_A);
else if (is_late(node, HSR_DEV_SLAVE_B))
hsr_nl_ringerror(hsr_priv, node->MacAddressA,
HSR_DEV_SLAVE_B);
rcu_read_lock();
port = get_late_port(hsr, node);
if (port != NULL)
hsr_nl_ringerror(hsr, node->MacAddressA, port);
rcu_read_unlock();
}
/* Prune old entries */
if (time_is_before_jiffies(timestamp +
msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
hsr_nl_nodedown(hsr_priv, node->MacAddressA);
hsr_nl_nodedown(hsr, node->MacAddressA);
list_del_rcu(&node->mac_list);
/* Note that we need to free this entry later: */
kfree_rcu(node, rcu_head);
@ -419,21 +407,21 @@ void hsr_prune_nodes(struct hsr_priv *hsr_priv)
}
void *hsr_get_next_node(struct hsr_priv *hsr_priv, void *_pos,
void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
unsigned char addr[ETH_ALEN])
{
struct node_entry *node;
struct hsr_node *node;
if (!_pos) {
node = list_first_or_null_rcu(&hsr_priv->node_db,
struct node_entry, mac_list);
node = list_first_or_null_rcu(&hsr->node_db,
struct hsr_node, mac_list);
if (node)
ether_addr_copy(addr, node->MacAddressA);
return node;
}
node = _pos;
list_for_each_entry_continue_rcu(node, &hsr_priv->node_db, mac_list) {
list_for_each_entry_continue_rcu(node, &hsr->node_db, mac_list) {
ether_addr_copy(addr, node->MacAddressA);
return node;
}
@ -442,7 +430,7 @@ void *hsr_get_next_node(struct hsr_priv *hsr_priv, void *_pos,
}
int hsr_get_node_data(struct hsr_priv *hsr_priv,
int hsr_get_node_data(struct hsr_priv *hsr,
const unsigned char *addr,
unsigned char addr_b[ETH_ALEN],
unsigned int *addr_b_ifindex,
@ -451,12 +439,13 @@ int hsr_get_node_data(struct hsr_priv *hsr_priv,
int *if2_age,
u16 *if2_seq)
{
struct node_entry *node;
struct hsr_node *node;
struct hsr_port *port;
unsigned long tdiff;
rcu_read_lock();
node = find_node_by_AddrA(&hsr_priv->node_db, addr);
node = find_node_by_AddrA(&hsr->node_db, addr);
if (!node) {
rcu_read_unlock();
return -ENOENT; /* No such entry */
@ -464,8 +453,8 @@ int hsr_get_node_data(struct hsr_priv *hsr_priv,
ether_addr_copy(addr_b, node->MacAddressB);
tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_A];
if (node->time_in_stale[HSR_DEV_SLAVE_A])
tdiff = jiffies - node->time_in[HSR_PT_SLAVE_A];
if (node->time_in_stale[HSR_PT_SLAVE_A])
*if1_age = INT_MAX;
#if HZ <= MSEC_PER_SEC
else if (tdiff > msecs_to_jiffies(INT_MAX))
@ -474,8 +463,8 @@ int hsr_get_node_data(struct hsr_priv *hsr_priv,
else
*if1_age = jiffies_to_msecs(tdiff);
tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_B];
if (node->time_in_stale[HSR_DEV_SLAVE_B])
tdiff = jiffies - node->time_in[HSR_PT_SLAVE_B];
if (node->time_in_stale[HSR_PT_SLAVE_B])
*if2_age = INT_MAX;
#if HZ <= MSEC_PER_SEC
else if (tdiff > msecs_to_jiffies(INT_MAX))
@ -485,13 +474,15 @@ int hsr_get_node_data(struct hsr_priv *hsr_priv,
*if2_age = jiffies_to_msecs(tdiff);
/* Present sequence numbers as if they were incoming on interface */
*if1_seq = node->seq_out[HSR_DEV_SLAVE_B];
*if2_seq = node->seq_out[HSR_DEV_SLAVE_A];
*if1_seq = node->seq_out[HSR_PT_SLAVE_B];
*if2_seq = node->seq_out[HSR_PT_SLAVE_A];
if ((node->AddrB_if != HSR_DEV_NONE) && hsr_priv->slave[node->AddrB_if])
*addr_b_ifindex = hsr_priv->slave[node->AddrB_if]->ifindex;
else
if (node->AddrB_port != HSR_PT_NONE) {
port = hsr_port_get_hsr(hsr, node->AddrB_port);
*addr_b_ifindex = port->dev->ifindex;
} else {
*addr_b_ifindex = -1;
}
rcu_read_unlock();

View file

@ -1,4 +1,4 @@
/* Copyright 2011-2013 Autronica Fire and Security AS
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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
@ -6,42 +6,43 @@
* any later version.
*
* Author(s):
* 2011-2013 Arvid Brodin, arvid.brodin@xdin.com
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#ifndef _HSR_FRAMEREG_H
#define _HSR_FRAMEREG_H
#ifndef __HSR_FRAMEREG_H
#define __HSR_FRAMEREG_H
#include "hsr_main.h"
struct node_entry;
struct hsr_node;
struct node_entry *hsr_find_node(struct list_head *node_db, struct sk_buff *skb);
struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[],
u16 seq_out);
struct hsr_node *hsr_get_node(struct list_head *node_db, struct sk_buff *skb,
bool is_sup);
void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
struct hsr_port *port);
bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr);
struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
struct node_entry *node,
struct sk_buff *skb,
enum hsr_dev_idx dev_idx);
void hsr_addr_subst_source(struct hsr_node *node, struct sk_buff *skb);
void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
struct hsr_port *port);
void hsr_addr_subst_source(struct hsr_priv *hsr_priv, struct sk_buff *skb);
void hsr_addr_subst_dest(struct hsr_priv *hsr_priv, struct ethhdr *ethhdr,
enum hsr_dev_idx dev_idx);
void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port,
u16 sequence_nr);
int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node,
u16 sequence_nr);
void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx dev_idx);
int hsr_register_frame_out(struct node_entry *node, enum hsr_dev_idx dev_idx,
struct sk_buff *skb);
void hsr_prune_nodes(struct hsr_priv *hsr_priv);
void hsr_prune_nodes(unsigned long data);
int hsr_create_self_node(struct list_head *self_node_db,
unsigned char addr_a[ETH_ALEN],
unsigned char addr_b[ETH_ALEN]);
void *hsr_get_next_node(struct hsr_priv *hsr_priv, void *_pos,
void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
unsigned char addr[ETH_ALEN]);
int hsr_get_node_data(struct hsr_priv *hsr_priv,
int hsr_get_node_data(struct hsr_priv *hsr,
const unsigned char *addr,
unsigned char addr_b[ETH_ALEN],
unsigned int *addr_b_ifindex,
@ -50,4 +51,4 @@ int hsr_get_node_data(struct hsr_priv *hsr_priv,
int *if2_age,
u16 *if2_seq);
#endif /* _HSR_FRAMEREG_H */
#endif /* __HSR_FRAMEREG_H */

View file

@ -1,4 +1,4 @@
/* Copyright 2011-2013 Autronica Fire and Security AS
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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
@ -6,11 +6,7 @@
* any later version.
*
* Author(s):
* 2011-2013 Arvid Brodin, arvid.brodin@xdin.com
*
* In addition to routines for registering and unregistering HSR support, this
* file also contains the receive routine that handles all incoming frames with
* Ethertype (protocol) ETH_P_PRP (HSRv0), and network device event handling.
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#include <linux/netdevice.h>
@ -21,154 +17,71 @@
#include "hsr_device.h"
#include "hsr_netlink.h"
#include "hsr_framereg.h"
/* List of all registered virtual HSR devices */
static LIST_HEAD(hsr_list);
void register_hsr_master(struct hsr_priv *hsr_priv)
{
list_add_tail_rcu(&hsr_priv->hsr_list, &hsr_list);
}
void unregister_hsr_master(struct hsr_priv *hsr_priv)
{
struct hsr_priv *hsr_priv_it;
list_for_each_entry(hsr_priv_it, &hsr_list, hsr_list)
if (hsr_priv_it == hsr_priv) {
list_del_rcu(&hsr_priv_it->hsr_list);
return;
}
}
bool is_hsr_slave(struct net_device *dev)
{
struct hsr_priv *hsr_priv_it;
list_for_each_entry_rcu(hsr_priv_it, &hsr_list, hsr_list) {
if (dev == hsr_priv_it->slave[0])
return true;
if (dev == hsr_priv_it->slave[1])
return true;
}
return false;
}
/* If dev is a HSR slave device, return the virtual master device. Return NULL
* otherwise.
*/
static struct hsr_priv *get_hsr_master(struct net_device *dev)
{
struct hsr_priv *hsr_priv;
rcu_read_lock();
list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list)
if ((dev == hsr_priv->slave[0]) ||
(dev == hsr_priv->slave[1])) {
rcu_read_unlock();
return hsr_priv;
}
rcu_read_unlock();
return NULL;
}
/* If dev is a HSR slave device, return the other slave device. Return NULL
* otherwise.
*/
static struct net_device *get_other_slave(struct hsr_priv *hsr_priv,
struct net_device *dev)
{
if (dev == hsr_priv->slave[0])
return hsr_priv->slave[1];
if (dev == hsr_priv->slave[1])
return hsr_priv->slave[0];
return NULL;
}
#include "hsr_slave.h"
static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct net_device *slave, *other_slave;
struct hsr_priv *hsr_priv;
int old_operstate;
struct net_device *dev;
struct hsr_port *port, *master;
struct hsr_priv *hsr;
int mtu_max;
int res;
struct net_device *dev;
dev = netdev_notifier_info_to_dev(ptr);
hsr_priv = get_hsr_master(dev);
if (hsr_priv) {
/* dev is a slave device */
slave = dev;
other_slave = get_other_slave(hsr_priv, slave);
} else {
port = hsr_port_get_rtnl(dev);
if (port == NULL) {
if (!is_hsr_master(dev))
return NOTIFY_DONE;
hsr_priv = netdev_priv(dev);
slave = hsr_priv->slave[0];
other_slave = hsr_priv->slave[1];
return NOTIFY_DONE; /* Not an HSR device */
hsr = netdev_priv(dev);
port = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
} else {
hsr = port->hsr;
}
switch (event) {
case NETDEV_UP: /* Administrative state DOWN */
case NETDEV_DOWN: /* Administrative state UP */
case NETDEV_CHANGE: /* Link (carrier) state changes */
old_operstate = hsr_priv->dev->operstate;
hsr_set_carrier(hsr_priv->dev, slave, other_slave);
/* netif_stacked_transfer_operstate() cannot be used here since
* it doesn't set IF_OPER_LOWERLAYERDOWN (?)
*/
hsr_set_operstate(hsr_priv->dev, slave, other_slave);
hsr_check_announce(hsr_priv->dev, old_operstate);
hsr_check_carrier_and_operstate(hsr);
break;
case NETDEV_CHANGEADDR:
/* This should not happen since there's no ndo_set_mac_address()
* for HSR devices - i.e. not supported.
*/
if (dev == hsr_priv->dev)
if (port->type == HSR_PT_MASTER) {
/* This should not happen since there's no
* ndo_set_mac_address() for HSR devices - i.e. not
* supported.
*/
break;
}
if (dev == hsr_priv->slave[0])
ether_addr_copy(hsr_priv->dev->dev_addr,
hsr_priv->slave[0]->dev_addr);
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
if (port->type == HSR_PT_SLAVE_A) {
ether_addr_copy(master->dev->dev_addr, dev->dev_addr);
call_netdevice_notifiers(NETDEV_CHANGEADDR, master->dev);
}
/* Make sure we recognize frames from ourselves in hsr_rcv() */
res = hsr_create_self_node(&hsr_priv->self_node_db,
hsr_priv->dev->dev_addr,
hsr_priv->slave[1] ?
hsr_priv->slave[1]->dev_addr :
hsr_priv->dev->dev_addr);
port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
res = hsr_create_self_node(&hsr->self_node_db,
master->dev->dev_addr,
port ?
port->dev->dev_addr :
master->dev->dev_addr);
if (res)
netdev_warn(hsr_priv->dev,
netdev_warn(master->dev,
"Could not update HSR node address.\n");
if (dev == hsr_priv->slave[0])
call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr_priv->dev);
break;
case NETDEV_CHANGEMTU:
if (dev == hsr_priv->dev)
if (port->type == HSR_PT_MASTER)
break; /* Handled in ndo_change_mtu() */
mtu_max = hsr_get_max_mtu(hsr_priv);
if (hsr_priv->dev->mtu > mtu_max)
dev_set_mtu(hsr_priv->dev, mtu_max);
mtu_max = hsr_get_max_mtu(port->hsr);
master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER);
master->dev->mtu = mtu_max;
break;
case NETDEV_UNREGISTER:
if (dev == hsr_priv->slave[0])
hsr_priv->slave[0] = NULL;
if (dev == hsr_priv->slave[1])
hsr_priv->slave[1] = NULL;
/* There should really be a way to set a new slave device... */
hsr_del_port(port);
break;
case NETDEV_PRE_TYPE_CHANGE:
/* HSR works only on Ethernet devices. Refuse slave to change
@ -181,255 +94,16 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
}
static struct timer_list prune_timer;
static void prune_nodes_all(unsigned long data)
struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt)
{
struct hsr_priv *hsr_priv;
struct hsr_port *port;
rcu_read_lock();
list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list)
hsr_prune_nodes(hsr_priv);
rcu_read_unlock();
prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD);
add_timer(&prune_timer);
}
static struct sk_buff *hsr_pull_tag(struct sk_buff *skb)
{
struct hsr_tag *hsr_tag;
struct sk_buff *skb2;
skb2 = skb_share_check(skb, GFP_ATOMIC);
if (unlikely(!skb2))
goto err_free;
skb = skb2;
if (unlikely(!pskb_may_pull(skb, HSR_TAGLEN)))
goto err_free;
hsr_tag = (struct hsr_tag *) skb->data;
skb->protocol = hsr_tag->encap_proto;
skb_pull(skb, HSR_TAGLEN);
return skb;
err_free:
kfree_skb(skb);
hsr_for_each_port(hsr, port)
if (port->type == pt)
return port;
return NULL;
}
/* The uses I can see for these HSR supervision frames are:
* 1) Use the frames that are sent after node initialization ("HSR_TLV.Type =
* 22") to reset any sequence_nr counters belonging to that node. Useful if
* the other node's counter has been reset for some reason.
* --
* Or not - resetting the counter and bridging the frame would create a
* loop, unfortunately.
*
* 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck
* frame is received from a particular node, we know something is wrong.
* We just register these (as with normal frames) and throw them away.
*
* 3) Allow different MAC addresses for the two slave interfaces, using the
* MacAddressA field.
*/
static bool is_supervision_frame(struct hsr_priv *hsr_priv, struct sk_buff *skb)
{
struct hsr_sup_tag *hsr_stag;
if (!ether_addr_equal(eth_hdr(skb)->h_dest,
hsr_priv->sup_multicast_addr))
return false;
hsr_stag = (struct hsr_sup_tag *) skb->data;
if (get_hsr_stag_path(hsr_stag) != 0x0f)
return false;
if ((hsr_stag->HSR_TLV_Type != HSR_TLV_ANNOUNCE) &&
(hsr_stag->HSR_TLV_Type != HSR_TLV_LIFE_CHECK))
return false;
if (hsr_stag->HSR_TLV_Length != 12)
return false;
return true;
}
/* Implementation somewhat according to IEC-62439-3, p. 43
*/
static int hsr_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
struct hsr_priv *hsr_priv;
struct net_device *other_slave;
struct node_entry *node;
bool deliver_to_self;
struct sk_buff *skb_deliver;
enum hsr_dev_idx dev_in_idx, dev_other_idx;
bool dup_out;
int ret;
hsr_priv = get_hsr_master(dev);
if (!hsr_priv) {
/* Non-HSR-slave device 'dev' is connected to a HSR network */
kfree_skb(skb);
dev->stats.rx_errors++;
return NET_RX_SUCCESS;
}
if (dev == hsr_priv->slave[0]) {
dev_in_idx = HSR_DEV_SLAVE_A;
dev_other_idx = HSR_DEV_SLAVE_B;
} else {
dev_in_idx = HSR_DEV_SLAVE_B;
dev_other_idx = HSR_DEV_SLAVE_A;
}
node = hsr_find_node(&hsr_priv->self_node_db, skb);
if (node) {
/* Always kill frames sent by ourselves */
kfree_skb(skb);
return NET_RX_SUCCESS;
}
/* Is this frame a candidate for local reception? */
deliver_to_self = false;
if ((skb->pkt_type == PACKET_HOST) ||
(skb->pkt_type == PACKET_MULTICAST) ||
(skb->pkt_type == PACKET_BROADCAST))
deliver_to_self = true;
else if (ether_addr_equal(eth_hdr(skb)->h_dest,
hsr_priv->dev->dev_addr)) {
skb->pkt_type = PACKET_HOST;
deliver_to_self = true;
}
rcu_read_lock(); /* node_db */
node = hsr_find_node(&hsr_priv->node_db, skb);
if (is_supervision_frame(hsr_priv, skb)) {
skb_pull(skb, sizeof(struct hsr_sup_tag));
node = hsr_merge_node(hsr_priv, node, skb, dev_in_idx);
if (!node) {
rcu_read_unlock(); /* node_db */
kfree_skb(skb);
hsr_priv->dev->stats.rx_dropped++;
return NET_RX_DROP;
}
skb_push(skb, sizeof(struct hsr_sup_tag));
deliver_to_self = false;
}
if (!node) {
/* Source node unknown; this might be a HSR frame from
* another net (different multicast address). Ignore it.
*/
rcu_read_unlock(); /* node_db */
kfree_skb(skb);
return NET_RX_SUCCESS;
}
/* Register ALL incoming frames as outgoing through the other interface.
* This allows us to register frames as incoming only if they are valid
* for the receiving interface, without using a specific counter for
* incoming frames.
*/
dup_out = hsr_register_frame_out(node, dev_other_idx, skb);
if (!dup_out)
hsr_register_frame_in(node, dev_in_idx);
/* Forward this frame? */
if (!dup_out && (skb->pkt_type != PACKET_HOST))
other_slave = get_other_slave(hsr_priv, dev);
else
other_slave = NULL;
if (hsr_register_frame_out(node, HSR_DEV_MASTER, skb))
deliver_to_self = false;
rcu_read_unlock(); /* node_db */
if (!deliver_to_self && !other_slave) {
kfree_skb(skb);
/* Circulated frame; silently remove it. */
return NET_RX_SUCCESS;
}
skb_deliver = skb;
if (deliver_to_self && other_slave) {
/* skb_clone() is not enough since we will strip the hsr tag
* and do address substitution below
*/
skb_deliver = pskb_copy(skb, GFP_ATOMIC);
if (!skb_deliver) {
deliver_to_self = false;
hsr_priv->dev->stats.rx_dropped++;
}
}
if (deliver_to_self) {
bool multicast_frame;
skb_deliver = hsr_pull_tag(skb_deliver);
if (!skb_deliver) {
hsr_priv->dev->stats.rx_dropped++;
goto forward;
}
#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
/* Move everything in the header that is after the HSR tag,
* to work around alignment problems caused by the 6-byte HSR
* tag. In practice, this removes/overwrites the HSR tag in
* the header and restores a "standard" packet.
*/
memmove(skb_deliver->data - HSR_TAGLEN, skb_deliver->data,
skb_headlen(skb_deliver));
/* Adjust skb members so they correspond with the move above.
* This cannot possibly underflow skb->data since hsr_pull_tag()
* above succeeded.
* At this point in the protocol stack, the transport and
* network headers have not been set yet, and we haven't touched
* the mac header nor the head. So we only need to adjust data
* and tail:
*/
skb_deliver->data -= HSR_TAGLEN;
skb_deliver->tail -= HSR_TAGLEN;
#endif
skb_deliver->dev = hsr_priv->dev;
hsr_addr_subst_source(hsr_priv, skb_deliver);
multicast_frame = (skb_deliver->pkt_type == PACKET_MULTICAST);
ret = netif_rx(skb_deliver);
if (ret == NET_RX_DROP) {
hsr_priv->dev->stats.rx_dropped++;
} else {
hsr_priv->dev->stats.rx_packets++;
hsr_priv->dev->stats.rx_bytes += skb->len;
if (multicast_frame)
hsr_priv->dev->stats.multicast++;
}
}
forward:
if (other_slave) {
skb_push(skb, ETH_HLEN);
skb->dev = other_slave;
dev_queue_xmit(skb);
}
return NET_RX_SUCCESS;
}
static struct packet_type hsr_pt __read_mostly = {
.type = htons(ETH_P_PRP),
.func = hsr_rcv,
};
static struct notifier_block hsr_nb = {
.notifier_call = hsr_netdev_notify, /* Slave event notifications */
};
@ -439,18 +113,9 @@ static int __init hsr_init(void)
{
int res;
BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_TAGLEN);
dev_add_pack(&hsr_pt);
init_timer(&prune_timer);
prune_timer.function = prune_nodes_all;
prune_timer.data = 0;
prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD);
add_timer(&prune_timer);
BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN);
register_netdevice_notifier(&hsr_nb);
res = hsr_netlink_init();
return res;
@ -459,9 +124,7 @@ static int __init hsr_init(void)
static void __exit hsr_exit(void)
{
unregister_netdevice_notifier(&hsr_nb);
del_timer_sync(&prune_timer);
hsr_netlink_exit();
dev_remove_pack(&hsr_pt);
}
module_init(hsr_init);

View file

@ -1,4 +1,4 @@
/* Copyright 2011-2013 Autronica Fire and Security AS
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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
@ -6,11 +6,11 @@
* any later version.
*
* Author(s):
* 2011-2013 Arvid Brodin, arvid.brodin@xdin.com
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#ifndef _HSR_PRIVATE_H
#define _HSR_PRIVATE_H
#ifndef __HSR_PRIVATE_H
#define __HSR_PRIVATE_H
#include <linux/netdevice.h>
#include <linux/list.h>
@ -29,6 +29,7 @@
* each node differ before we notify of communication problem?
*/
#define MAX_SLAVE_DIFF 3000 /* ms */
#define HSR_SEQNR_START (USHRT_MAX - 1024)
/* How often shall we check for broken ring and remove node entries older than
@ -46,16 +47,16 @@
* path, LSDU_size, sequence Nr }. But we let eth_header() create { h_dest,
* h_source, h_proto = 0x88FB }, and add { path, LSDU_size, sequence Nr,
* encapsulated protocol } instead.
*
* Field names as defined in the IEC:2010 standard for HSR.
*/
#define HSR_TAGLEN 6
/* Field names below as defined in the IEC:2010 standard for HSR. */
struct hsr_tag {
__be16 path_and_LSDU_size;
__be16 sequence_nr;
__be16 encap_proto;
} __packed;
#define HSR_HLEN 6
/* The helper functions below assumes that 'path' occupies the 4 most
* significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or
@ -136,31 +137,47 @@ struct hsr_ethhdr_sp {
} __packed;
enum hsr_dev_idx {
HSR_DEV_NONE = -1,
HSR_DEV_SLAVE_A = 0,
HSR_DEV_SLAVE_B,
HSR_DEV_MASTER,
enum hsr_port_type {
HSR_PT_NONE = 0, /* Must be 0, used by framereg */
HSR_PT_SLAVE_A,
HSR_PT_SLAVE_B,
HSR_PT_INTERLINK,
HSR_PT_MASTER,
HSR_PT_PORTS, /* This must be the last item in the enum */
};
struct hsr_port {
struct list_head port_list;
struct net_device *dev;
struct hsr_priv *hsr;
enum hsr_port_type type;
};
#define HSR_MAX_SLAVE (HSR_DEV_SLAVE_B + 1)
#define HSR_MAX_DEV (HSR_DEV_MASTER + 1)
struct hsr_priv {
struct list_head hsr_list; /* List of hsr devices */
struct rcu_head rcu_head;
struct net_device *dev;
struct net_device *slave[HSR_MAX_SLAVE];
struct list_head node_db; /* Other HSR nodes */
struct list_head ports;
struct list_head node_db; /* Known HSR nodes */
struct list_head self_node_db; /* MACs of slaves */
struct timer_list announce_timer; /* Supervision frame dispatch */
struct timer_list prune_timer;
int announce_count;
u16 sequence_nr;
spinlock_t seqnr_lock; /* locking for sequence_nr */
unsigned char sup_multicast_addr[ETH_ALEN];
};
void register_hsr_master(struct hsr_priv *hsr_priv);
void unregister_hsr_master(struct hsr_priv *hsr_priv);
bool is_hsr_slave(struct net_device *dev);
#define hsr_for_each_port(hsr, port) \
list_for_each_entry_rcu((port), &(hsr)->ports, port_list)
#endif /* _HSR_PRIVATE_H */
struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt);
/* Caller must ensure skb is a valid HSR frame */
static inline u16 hsr_get_skb_sequence_nr(struct sk_buff *skb)
{
struct hsr_ethhdr *hsr_ethhdr;
hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb);
return ntohs(hsr_ethhdr->hsr_tag.sequence_nr);
}
#endif /* __HSR_PRIVATE_H */

View file

@ -1,4 +1,4 @@
/* Copyright 2011-2013 Autronica Fire and Security AS
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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
@ -6,7 +6,7 @@
* any later version.
*
* Author(s):
* 2011-2013 Arvid Brodin, arvid.brodin@xdin.com
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*
* Routines for handling Netlink messages for HSR.
*/
@ -37,13 +37,17 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
struct net_device *link[2];
unsigned char multicast_spec;
if (!data) {
netdev_info(dev, "HSR: No slave devices specified\n");
return -EINVAL;
}
if (!data[IFLA_HSR_SLAVE1]) {
netdev_info(dev, "IFLA_HSR_SLAVE1 missing!\n");
netdev_info(dev, "HSR: Slave1 device not specified\n");
return -EINVAL;
}
link[0] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE1]));
if (!data[IFLA_HSR_SLAVE2]) {
netdev_info(dev, "IFLA_HSR_SLAVE2 missing!\n");
netdev_info(dev, "HSR: Slave2 device not specified\n");
return -EINVAL;
}
link[1] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE2]));
@ -63,21 +67,33 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
static int hsr_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
struct hsr_priv *hsr_priv;
struct hsr_priv *hsr;
struct hsr_port *port;
int res;
hsr_priv = netdev_priv(dev);
hsr = netdev_priv(dev);
if (hsr_priv->slave[0])
if (nla_put_u32(skb, IFLA_HSR_SLAVE1, hsr_priv->slave[0]->ifindex))
goto nla_put_failure;
res = 0;
if (hsr_priv->slave[1])
if (nla_put_u32(skb, IFLA_HSR_SLAVE2, hsr_priv->slave[1]->ifindex))
goto nla_put_failure;
rcu_read_lock();
port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
if (port)
res = nla_put_u32(skb, IFLA_HSR_SLAVE1, port->dev->ifindex);
rcu_read_unlock();
if (res)
goto nla_put_failure;
rcu_read_lock();
port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
if (port)
res = nla_put_u32(skb, IFLA_HSR_SLAVE2, port->dev->ifindex);
rcu_read_unlock();
if (res)
goto nla_put_failure;
if (nla_put(skb, IFLA_HSR_SUPERVISION_ADDR, ETH_ALEN,
hsr_priv->sup_multicast_addr) ||
nla_put_u16(skb, IFLA_HSR_SEQ_NR, hsr_priv->sequence_nr))
hsr->sup_multicast_addr) ||
nla_put_u16(skb, IFLA_HSR_SEQ_NR, hsr->sequence_nr))
goto nla_put_failure;
return 0;
@ -128,13 +144,13 @@ static const struct genl_multicast_group hsr_mcgrps[] = {
* over one of the slave interfaces. This would indicate an open network ring
* (i.e. a link has failed somewhere).
*/
void hsr_nl_ringerror(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
enum hsr_dev_idx dev_idx)
void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN],
struct hsr_port *port)
{
struct sk_buff *skb;
void *msg_head;
struct hsr_port *master;
int res;
int ifindex;
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
if (!skb)
@ -148,11 +164,7 @@ void hsr_nl_ringerror(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
if (res < 0)
goto nla_put_failure;
if (hsr_priv->slave[dev_idx])
ifindex = hsr_priv->slave[dev_idx]->ifindex;
else
ifindex = -1;
res = nla_put_u32(skb, HSR_A_IFINDEX, ifindex);
res = nla_put_u32(skb, HSR_A_IFINDEX, port->dev->ifindex);
if (res < 0)
goto nla_put_failure;
@ -165,16 +177,20 @@ nla_put_failure:
kfree_skb(skb);
fail:
netdev_warn(hsr_priv->dev, "Could not send HSR ring error message\n");
rcu_read_lock();
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
netdev_warn(master->dev, "Could not send HSR ring error message\n");
rcu_read_unlock();
}
/* This is called when we haven't heard from the node with MAC address addr for
* some time (just before the node is removed from the node table/list).
*/
void hsr_nl_nodedown(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN])
void hsr_nl_nodedown(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN])
{
struct sk_buff *skb;
void *msg_head;
struct hsr_port *master;
int res;
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
@ -199,7 +215,10 @@ nla_put_failure:
kfree_skb(skb);
fail:
netdev_warn(hsr_priv->dev, "Could not send HSR node down\n");
rcu_read_lock();
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
netdev_warn(master->dev, "Could not send HSR node down\n");
rcu_read_unlock();
}
@ -220,7 +239,8 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
/* For sending */
struct sk_buff *skb_out;
void *msg_head;
struct hsr_priv *hsr_priv;
struct hsr_priv *hsr;
struct hsr_port *port;
unsigned char hsr_node_addr_b[ETH_ALEN];
int hsr_node_if1_age;
u16 hsr_node_if1_seq;
@ -267,8 +287,8 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
if (res < 0)
goto nla_put_failure;
hsr_priv = netdev_priv(hsr_dev);
res = hsr_get_node_data(hsr_priv,
hsr = netdev_priv(hsr_dev);
res = hsr_get_node_data(hsr,
(unsigned char *) nla_data(info->attrs[HSR_A_NODE_ADDR]),
hsr_node_addr_b,
&addr_b_ifindex,
@ -301,9 +321,12 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq);
if (res < 0)
goto nla_put_failure;
if (hsr_priv->slave[0])
rcu_read_lock();
port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
if (port)
res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX,
hsr_priv->slave[0]->ifindex);
port->dev->ifindex);
rcu_read_unlock();
if (res < 0)
goto nla_put_failure;
@ -313,9 +336,14 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq);
if (res < 0)
goto nla_put_failure;
if (hsr_priv->slave[1])
rcu_read_lock();
port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
if (port)
res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX,
hsr_priv->slave[1]->ifindex);
port->dev->ifindex);
rcu_read_unlock();
if (res < 0)
goto nla_put_failure;
genlmsg_end(skb_out, msg_head);
genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid);
@ -334,7 +362,7 @@ fail:
return res;
}
/* Get a list of MacAddressA of all nodes known to this node (other than self).
/* Get a list of MacAddressA of all nodes known to this node (including self).
*/
static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
{
@ -345,7 +373,7 @@ static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
/* For sending */
struct sk_buff *skb_out;
void *msg_head;
struct hsr_priv *hsr_priv;
struct hsr_priv *hsr;
void *pos;
unsigned char addr[ETH_ALEN];
int res;
@ -385,17 +413,17 @@ static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
if (res < 0)
goto nla_put_failure;
hsr_priv = netdev_priv(hsr_dev);
hsr = netdev_priv(hsr_dev);
rcu_read_lock();
pos = hsr_get_next_node(hsr_priv, NULL, addr);
pos = hsr_get_next_node(hsr, NULL, addr);
while (pos) {
res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, addr);
if (res < 0) {
rcu_read_unlock();
goto nla_put_failure;
}
pos = hsr_get_next_node(hsr_priv, pos, addr);
pos = hsr_get_next_node(hsr, pos, addr);
}
rcu_read_unlock();

View file

@ -1,4 +1,4 @@
/* Copyright 2011-2013 Autronica Fire and Security AS
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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
@ -6,7 +6,7 @@
* any later version.
*
* Author(s):
* 2011-2013 Arvid Brodin, arvid.brodin@xdin.com
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#ifndef __HSR_NETLINK_H
@ -17,13 +17,14 @@
#include <uapi/linux/hsr_netlink.h>
struct hsr_priv;
struct hsr_port;
int __init hsr_netlink_init(void);
void __exit hsr_netlink_exit(void);
void hsr_nl_ringerror(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN],
int dev_idx);
void hsr_nl_nodedown(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN]);
void hsr_nl_ringerror(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN],
struct hsr_port *port);
void hsr_nl_nodedown(struct hsr_priv *hsr, unsigned char addr[ETH_ALEN]);
void hsr_nl_framedrop(int dropcount, int dev_idx);
void hsr_nl_linkdown(int dev_idx);

196
net/hsr/hsr_slave.c Normal file
View file

@ -0,0 +1,196 @@
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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.
*
* Author(s):
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#include "hsr_slave.h"
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include "hsr_main.h"
#include "hsr_device.h"
#include "hsr_forward.h"
#include "hsr_framereg.h"
static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct hsr_port *port;
if (!skb_mac_header_was_set(skb)) {
WARN_ONCE(1, "%s: skb invalid", __func__);
return RX_HANDLER_PASS;
}
rcu_read_lock(); /* hsr->node_db, hsr->ports */
port = hsr_port_get_rcu(skb->dev);
if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) {
/* Directly kill frames sent by ourselves */
kfree_skb(skb);
goto finish_consume;
}
if (eth_hdr(skb)->h_proto != htons(ETH_P_PRP))
goto finish_pass;
skb_push(skb, ETH_HLEN);
hsr_forward_skb(skb, port);
finish_consume:
rcu_read_unlock(); /* hsr->node_db, hsr->ports */
return RX_HANDLER_CONSUMED;
finish_pass:
rcu_read_unlock(); /* hsr->node_db, hsr->ports */
return RX_HANDLER_PASS;
}
bool hsr_port_exists(const struct net_device *dev)
{
return rcu_access_pointer(dev->rx_handler) == hsr_handle_frame;
}
static int hsr_check_dev_ok(struct net_device *dev)
{
/* Don't allow HSR on non-ethernet like devices */
if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) ||
(dev->addr_len != ETH_ALEN)) {
netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n");
return -EINVAL;
}
/* Don't allow enslaving hsr devices */
if (is_hsr_master(dev)) {
netdev_info(dev, "Cannot create trees of HSR devices.\n");
return -EINVAL;
}
if (hsr_port_exists(dev)) {
netdev_info(dev, "This device is already a HSR slave.\n");
return -EINVAL;
}
if (dev->priv_flags & IFF_802_1Q_VLAN) {
netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n");
return -EINVAL;
}
if (dev->priv_flags & IFF_DONT_BRIDGE) {
netdev_info(dev, "This device does not support bridging.\n");
return -EOPNOTSUPP;
}
/* HSR over bonded devices has not been tested, but I'm not sure it
* won't work...
*/
return 0;
}
/* Setup device to be added to the HSR bridge. */
static int hsr_portdev_setup(struct net_device *dev, struct hsr_port *port)
{
int res;
dev_hold(dev);
res = dev_set_promiscuity(dev, 1);
if (res)
goto fail_promiscuity;
/* FIXME:
* What does net device "adjacency" mean? Should we do
* res = netdev_master_upper_dev_link(port->dev, port->hsr->dev); ?
*/
res = netdev_rx_handler_register(dev, hsr_handle_frame, port);
if (res)
goto fail_rx_handler;
dev_disable_lro(dev);
return 0;
fail_rx_handler:
dev_set_promiscuity(dev, -1);
fail_promiscuity:
dev_put(dev);
return res;
}
int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev,
enum hsr_port_type type)
{
struct hsr_port *port, *master;
int res;
if (type != HSR_PT_MASTER) {
res = hsr_check_dev_ok(dev);
if (res)
return res;
}
port = hsr_port_get_hsr(hsr, type);
if (port != NULL)
return -EBUSY; /* This port already exists */
port = kzalloc(sizeof(*port), GFP_KERNEL);
if (port == NULL)
return -ENOMEM;
if (type != HSR_PT_MASTER) {
res = hsr_portdev_setup(dev, port);
if (res)
goto fail_dev_setup;
}
port->hsr = hsr;
port->dev = dev;
port->type = type;
list_add_tail_rcu(&port->port_list, &hsr->ports);
synchronize_rcu();
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
netdev_update_features(master->dev);
dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
return 0;
fail_dev_setup:
kfree(port);
return res;
}
void hsr_del_port(struct hsr_port *port)
{
struct hsr_priv *hsr;
struct hsr_port *master;
hsr = port->hsr;
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
list_del_rcu(&port->port_list);
if (port != master) {
netdev_update_features(master->dev);
dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
netdev_rx_handler_unregister(port->dev);
dev_set_promiscuity(port->dev, -1);
}
/* FIXME?
* netdev_upper_dev_unlink(port->dev, port->hsr->dev);
*/
synchronize_rcu();
dev_put(port->dev);
}

38
net/hsr/hsr_slave.h Normal file
View file

@ -0,0 +1,38 @@
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* 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.
*
* Author(s):
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#ifndef __HSR_SLAVE_H
#define __HSR_SLAVE_H
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include "hsr_main.h"
int hsr_add_port(struct hsr_priv *hsr, struct net_device *dev,
enum hsr_port_type pt);
void hsr_del_port(struct hsr_port *port);
bool hsr_port_exists(const struct net_device *dev);
static inline struct hsr_port *hsr_port_get_rtnl(const struct net_device *dev)
{
ASSERT_RTNL();
return hsr_port_exists(dev) ?
rtnl_dereference(dev->rx_handler_data) : NULL;
}
static inline struct hsr_port *hsr_port_get_rcu(const struct net_device *dev)
{
return hsr_port_exists(dev) ?
rcu_dereference(dev->rx_handler_data) : NULL;
}
#endif /* __HSR_SLAVE_H */

View file

@ -80,14 +80,14 @@ lowpan_dev_info *lowpan_dev_info(const struct net_device *dev)
static inline void lowpan_address_flip(u8 *src, u8 *dest)
{
int i;
for (i = 0; i < IEEE802154_ADDR_LEN; i++)
(dest)[IEEE802154_ADDR_LEN - i - 1] = (src)[i];
}
static int lowpan_header_create(struct sk_buff *skb,
struct net_device *dev,
unsigned short type, const void *_daddr,
const void *_saddr, unsigned int len)
static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *_daddr,
const void *_saddr, unsigned int len)
{
const u8 *saddr = _saddr;
const u8 *daddr = _daddr;
@ -144,7 +144,7 @@ static int lowpan_header_create(struct sk_buff *skb,
}
static int lowpan_give_skb_to_devices(struct sk_buff *skb,
struct net_device *dev)
struct net_device *dev)
{
struct lowpan_dev_record *entry;
struct sk_buff *skb_cp;
@ -368,24 +368,28 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
static struct wpan_phy *lowpan_get_phy(const struct net_device *dev)
{
struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
return ieee802154_mlme_ops(real_dev)->get_phy(real_dev);
}
static __le16 lowpan_get_pan_id(const struct net_device *dev)
{
struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev);
}
static __le16 lowpan_get_short_addr(const struct net_device *dev)
{
struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev);
}
static u8 lowpan_get_dsn(const struct net_device *dev)
{
struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev);
}
@ -454,7 +458,7 @@ static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[])
}
static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
struct packet_type *pt, struct net_device *orig_dev)
{
struct ieee802154_hdr hdr;
int ret;

View file

@ -12,13 +12,6 @@ config IEEE802154
config IEEE802154_6LOWPAN
tristate "6lowpan support over IEEE 802.15.4"
depends on IEEE802154 && IPV6
select 6LOWPAN_IPHC
depends on IEEE802154 && 6LOWPAN
---help---
IPv6 compression over IEEE 802.15.4.
config 6LOWPAN_IPHC
tristate
---help---
6lowpan compression code which is shared between IEEE 802.15.4 and Bluetooth
stacks.

View file

@ -1,8 +1,7 @@
obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o
obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o
obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
6lowpan-y := 6lowpan_rtnl.o reassembly.o
ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \
header_ops.o
af_802154-y := af_ieee802154.o raw.o dgram.o

View file

@ -40,9 +40,7 @@
#include "af802154.h"
/*
* Utility function for families
*/
/* Utility function for families */
struct net_device*
ieee802154_get_dev(struct net *net, const struct ieee802154_addr *addr)
{
@ -87,8 +85,8 @@ ieee802154_get_dev(struct net *net, const struct ieee802154_addr *addr)
rtnl_unlock();
break;
default:
pr_warning("Unsupported ieee802154 address type: %d\n",
addr->mode);
pr_warn("Unsupported ieee802154 address type: %d\n",
addr->mode);
break;
}
@ -106,7 +104,7 @@ static int ieee802154_sock_release(struct socket *sock)
return 0;
}
static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len)
struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
@ -114,7 +112,7 @@ static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
}
static int ieee802154_sock_bind(struct socket *sock, struct sockaddr *uaddr,
int addr_len)
int addr_len)
{
struct sock *sk = sock->sk;
@ -125,7 +123,7 @@ static int ieee802154_sock_bind(struct socket *sock, struct sockaddr *uaddr,
}
static int ieee802154_sock_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
int addr_len, int flags)
{
struct sock *sk = sock->sk;
@ -139,7 +137,7 @@ static int ieee802154_sock_connect(struct socket *sock, struct sockaddr *uaddr,
}
static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
unsigned int cmd)
unsigned int cmd)
{
struct ifreq ifr;
int ret = -ENOIOCTLCMD;
@ -167,7 +165,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
}
static int ieee802154_sock_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
unsigned long arg)
{
struct sock *sk = sock->sk;
@ -238,8 +236,7 @@ static const struct proto_ops ieee802154_dgram_ops = {
};
/*
* Create a socket. Initialise the socket, blank the addresses
/* Create a socket. Initialise the socket, blank the addresses
* set the state.
*/
static int ieee802154_create(struct net *net, struct socket *sock,
@ -301,13 +298,14 @@ static const struct net_proto_family ieee802154_family_ops = {
};
static int ieee802154_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
struct packet_type *pt, struct net_device *orig_dev)
{
if (!netif_running(dev))
goto drop;
pr_debug("got frame, type %d, dev %p\n", dev->type, dev);
#ifdef DEBUG
print_hex_dump_bytes("ieee802154_rcv ", DUMP_PREFIX_NONE, skb->data, skb->len);
print_hex_dump_bytes("ieee802154_rcv ",
DUMP_PREFIX_NONE, skb->data, skb->len);
#endif
if (!net_eq(dev_net(dev), &init_net))

View file

@ -149,8 +149,7 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
spin_lock_bh(&sk->sk_receive_queue.lock);
skb = skb_peek(&sk->sk_receive_queue);
if (skb != NULL) {
/*
* We will only return the amount
/* We will only return the amount
* of this packet since that is all
* that will be read.
*/
@ -161,12 +160,13 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
}
}
return -ENOIOCTLCMD;
}
/* FIXME: autobind */
static int dgram_connect(struct sock *sk, struct sockaddr *uaddr,
int len)
int len)
{
struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
struct dgram_sock *ro = dgram_sk(sk);
@ -205,7 +205,7 @@ static int dgram_disconnect(struct sock *sk, int flags)
}
static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t size)
struct msghdr *msg, size_t size)
{
struct net_device *dev;
unsigned int mtu;
@ -248,8 +248,8 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
hlen = LL_RESERVED_SPACE(dev);
tlen = dev->needed_tailroom;
skb = sock_alloc_send_skb(sk, hlen + tlen + size,
msg->msg_flags & MSG_DONTWAIT,
&err);
msg->msg_flags & MSG_DONTWAIT,
&err);
if (!skb)
goto out_dev;
@ -262,7 +262,8 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
cb->ackreq = ro->want_ack;
if (msg->msg_name) {
DECLARE_SOCKADDR(struct sockaddr_ieee802154*, daddr, msg->msg_name);
DECLARE_SOCKADDR(struct sockaddr_ieee802154*,
daddr, msg->msg_name);
ieee802154_addr_from_sa(&dst_addr, &daddr->addr);
} else {
@ -304,8 +305,8 @@ out:
}
static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len, int noblock, int flags,
int *addr_len)
struct msghdr *msg, size_t len, int noblock,
int flags, int *addr_len)
{
size_t copied = 0;
int err = -EOPNOTSUPP;
@ -398,6 +399,7 @@ int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
dgram_sk(sk))) {
if (prev) {
struct sk_buff *clone;
clone = skb_clone(skb, GFP_ATOMIC);
if (clone)
dgram_rcv_skb(prev, clone);
@ -407,9 +409,9 @@ int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
}
}
if (prev)
if (prev) {
dgram_rcv_skb(prev, skb);
else {
} else {
kfree_skb(skb);
ret = NET_RX_DROP;
}
@ -419,7 +421,7 @@ int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
}
static int dgram_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
char __user *optval, int __user *optlen)
{
struct dgram_sock *ro = dgram_sk(sk);
@ -463,7 +465,7 @@ static int dgram_getsockopt(struct sock *sk, int level, int optname,
}
static int dgram_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, unsigned int optlen)
char __user *optval, unsigned int optlen)
{
struct dgram_sock *ro = dgram_sk(sk);
struct net *net = sock_net(sk);

View file

@ -43,7 +43,7 @@ struct genl_info;
struct sk_buff *ieee802154_nl_create(int flags, u8 req);
int ieee802154_nl_mcast(struct sk_buff *msg, unsigned int group);
struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,
int flags, u8 req);
int flags, u8 req);
int ieee802154_nl_reply(struct sk_buff *msg, struct genl_info *info);
extern struct genl_family nl802154_family;

View file

@ -52,7 +52,7 @@ struct sk_buff *ieee802154_nl_create(int flags, u8 req)
spin_lock_irqsave(&ieee802154_seq_lock, f);
hdr = genlmsg_put(msg, 0, ieee802154_seq_num++,
&nl802154_family, flags, req);
&nl802154_family, flags, req);
spin_unlock_irqrestore(&ieee802154_seq_lock, f);
if (!hdr) {
nlmsg_free(msg);
@ -86,7 +86,7 @@ struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,
return NULL;
hdr = genlmsg_put_reply(msg, info,
&nl802154_family, flags, req);
&nl802154_family, flags, req);
if (!hdr) {
nlmsg_free(msg);
return NULL;

Some files were not shown because too many files have changed in this diff Show more