Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
3f5d6af0
Commit
3f5d6af0
authored
Jun 25, 2013
by
Stephen Hemminger
Browse files
Options
Browse Files
Download
Plain Diff
Merge ../vxlan-x
parents
8599b52e
537f7f84
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
359 additions
and
216 deletions
+359
-216
drivers/net/vxlan.c
drivers/net/vxlan.c
+354
-208
net/bridge/br_fdb.c
net/bridge/br_fdb.c
+5
-0
net/core/rtnetlink.c
net/core/rtnetlink.c
+0
-8
No files found.
drivers/net/vxlan.c
View file @
3f5d6af0
...
...
@@ -68,24 +68,26 @@ struct vxlanhdr {
/* UDP port for VXLAN traffic.
* The IANA assigned port is 4789, but the Linux default is 8472
* for compat
a
bility with early adopters.
* for compat
i
bility with early adopters.
*/
static
unsigned
in
t
vxlan_port
__read_mostly
=
8472
;
module_param_named
(
udp_port
,
vxlan_port
,
u
in
t
,
0444
);
static
unsigned
shor
t
vxlan_port
__read_mostly
=
8472
;
module_param_named
(
udp_port
,
vxlan_port
,
u
shor
t
,
0444
);
MODULE_PARM_DESC
(
udp_port
,
"Destination UDP port"
);
static
bool
log_ecn_error
=
true
;
module_param
(
log_ecn_error
,
bool
,
0644
);
MODULE_PARM_DESC
(
log_ecn_error
,
"Log packets received with corrupted ECN"
);
static
unsigned
int
vxlan_net_id
;
static
int
vxlan_net_id
;
static
const
u8
all_zeros_mac
[
ETH_ALEN
];
/* per UDP socket information */
struct
vxlan_sock
{
struct
hlist_node
hlist
;
struct
rcu_head
rcu
;
struct
work_struct
del_work
;
unsigned
in
t
refcnt
;
atomic_
t
refcnt
;
struct
socket
*
sock
;
struct
hlist_head
vni_list
[
VNI_HASH_SIZE
];
};
...
...
@@ -94,6 +96,7 @@ struct vxlan_sock {
struct
vxlan_net
{
struct
list_head
vxlan_list
;
struct
hlist_head
sock_list
[
PORT_HASH_SIZE
];
spinlock_t
sock_lock
;
};
struct
vxlan_rdst
{
...
...
@@ -101,7 +104,8 @@ struct vxlan_rdst {
__be16
remote_port
;
u32
remote_vni
;
u32
remote_ifindex
;
struct
vxlan_rdst
*
remote_next
;
struct
list_head
list
;
struct
rcu_head
rcu
;
};
/* Forwarding table entry */
...
...
@@ -110,7 +114,7 @@ struct vxlan_fdb {
struct
rcu_head
rcu
;
unsigned
long
updated
;
/* jiffies */
unsigned
long
used
;
struct
vxlan_rdst
remote
;
struct
list_head
remotes
;
u16
state
;
/* see ndm_state */
u8
flags
;
/* see ndm_flags */
u8
eth_addr
[
ETH_ALEN
];
...
...
@@ -131,6 +135,9 @@ struct vxlan_dev {
__u8
ttl
;
u32
flags
;
/* VXLAN_F_* below */
struct
work_struct
sock_work
;
struct
work_struct
igmp_work
;
unsigned
long
age_interval
;
struct
timer_list
age_timer
;
spinlock_t
hash_lock
;
...
...
@@ -148,6 +155,9 @@ struct vxlan_dev {
/* salt for hash table */
static
u32
vxlan_salt
__read_mostly
;
static
struct
workqueue_struct
*
vxlan_wq
;
static
void
vxlan_sock_work
(
struct
work_struct
*
work
);
/* Virtual Network hash table head */
static
inline
struct
hlist_head
*
vni_head
(
struct
vxlan_sock
*
vs
,
u32
id
)
...
...
@@ -163,6 +173,14 @@ static inline struct hlist_head *vs_head(struct net *net, __be16 port)
return
&
vn
->
sock_list
[
hash_32
(
ntohs
(
port
),
PORT_HASH_BITS
)];
}
/* First remote destination for a forwarding entry.
* Guaranteed to be non-NULL because remotes are never deleted.
*/
static
inline
struct
vxlan_rdst
*
first_remote
(
struct
vxlan_fdb
*
fdb
)
{
return
list_first_or_null_rcu
(
&
fdb
->
remotes
,
struct
vxlan_rdst
,
list
);
}
/* Find VXLAN socket based on network namespace and UDP port */
static
struct
vxlan_sock
*
vxlan_find_port
(
struct
net
*
net
,
__be16
port
)
{
...
...
@@ -195,9 +213,9 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, __be16 port)
/* Fill in neighbour message in skbuff. */
static
int
vxlan_fdb_info
(
struct
sk_buff
*
skb
,
struct
vxlan_dev
*
vxlan
,
const
struct
vxlan_fdb
*
fdb
,
u32
portid
,
u32
seq
,
int
type
,
unsigned
int
flags
,
const
struct
vxlan_rdst
*
rdst
)
const
struct
vxlan_fdb
*
fdb
,
u32
portid
,
u32
seq
,
int
type
,
unsigned
int
flags
,
const
struct
vxlan_rdst
*
rdst
)
{
unsigned
long
now
=
jiffies
;
struct
nda_cacheinfo
ci
;
...
...
@@ -235,7 +253,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
nla_put_be16
(
skb
,
NDA_PORT
,
rdst
->
remote_port
))
goto
nla_put_failure
;
if
(
rdst
->
remote_vni
!=
vxlan
->
default_dst
.
remote_vni
&&
nla_put_
be
32
(
skb
,
NDA_VNI
,
rdst
->
remote_vni
))
nla_put_
u
32
(
skb
,
NDA_VNI
,
rdst
->
remote_vni
))
goto
nla_put_failure
;
if
(
rdst
->
remote_ifindex
&&
nla_put_u32
(
skb
,
NDA_IFINDEX
,
rdst
->
remote_ifindex
))
...
...
@@ -268,7 +286,7 @@ static inline size_t vxlan_nlmsg_size(void)
}
static
void
vxlan_fdb_notify
(
struct
vxlan_dev
*
vxlan
,
const
struct
vxlan_fdb
*
fdb
,
int
type
)
struct
vxlan_fdb
*
fdb
,
int
type
)
{
struct
net
*
net
=
dev_net
(
vxlan
->
dev
);
struct
sk_buff
*
skb
;
...
...
@@ -278,7 +296,7 @@ static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
if
(
skb
==
NULL
)
goto
errout
;
err
=
vxlan_fdb_info
(
skb
,
vxlan
,
fdb
,
0
,
0
,
type
,
0
,
&
fdb
->
remote
);
err
=
vxlan_fdb_info
(
skb
,
vxlan
,
fdb
,
0
,
0
,
type
,
0
,
first_remote
(
fdb
)
);
if
(
err
<
0
)
{
/* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */
WARN_ON
(
err
==
-
EMSGSIZE
);
...
...
@@ -296,22 +314,27 @@ static void vxlan_fdb_notify(struct vxlan_dev *vxlan,
static
void
vxlan_ip_miss
(
struct
net_device
*
dev
,
__be32
ipa
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
vxlan_fdb
f
;
struct
vxlan_fdb
f
=
{
.
state
=
NUD_STALE
,
};
struct
vxlan_rdst
remote
=
{
.
remote_ip
=
ipa
,
/* goes to NDA_DST */
.
remote_vni
=
VXLAN_N_VID
,
};
memset
(
&
f
,
0
,
sizeof
f
);
f
.
state
=
NUD_STALE
;
f
.
remote
.
remote_ip
=
ipa
;
/* goes to NDA_DST */
f
.
remote
.
remote_vni
=
VXLAN_N_VID
;
INIT_LIST_HEAD
(
&
f
.
remotes
);
list_add_rcu
(
&
remote
.
list
,
&
f
.
remotes
);
vxlan_fdb_notify
(
vxlan
,
&
f
,
RTM_GETNEIGH
);
}
static
void
vxlan_fdb_miss
(
struct
vxlan_dev
*
vxlan
,
const
u8
eth_addr
[
ETH_ALEN
])
{
struct
vxlan_fdb
f
;
struct
vxlan_fdb
f
=
{
.
state
=
NUD_STALE
,
};
memset
(
&
f
,
0
,
sizeof
f
);
f
.
state
=
NUD_STALE
;
INIT_LIST_HEAD
(
&
f
.
remotes
);
memcpy
(
f
.
eth_addr
,
eth_addr
,
ETH_ALEN
);
vxlan_fdb_notify
(
vxlan
,
&
f
,
RTM_GETNEIGH
);
...
...
@@ -366,21 +389,34 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
return
f
;
}
/* Add/update destinations for multicast */
static
int
vxlan_fdb_append
(
struct
vxlan_fdb
*
f
,
__be32
ip
,
__be16
port
,
__u32
vni
,
__u32
ifindex
)
/* caller should hold vxlan->hash_lock */
static
struct
vxlan_rdst
*
vxlan_fdb_find_rdst
(
struct
vxlan_fdb
*
f
,
__be32
ip
,
__be16
port
,
__u32
vni
,
__u32
ifindex
)
{
struct
vxlan_rdst
*
rd
_prev
,
*
rd
;
struct
vxlan_rdst
*
rd
;
rd_prev
=
NULL
;
for
(
rd
=
&
f
->
remote
;
rd
;
rd
=
rd
->
remote_next
)
{
list_for_each_entry
(
rd
,
&
f
->
remotes
,
list
)
{
if
(
rd
->
remote_ip
==
ip
&&
rd
->
remote_port
==
port
&&
rd
->
remote_vni
==
vni
&&
rd
->
remote_ifindex
==
ifindex
)
return
0
;
rd_prev
=
rd
;
return
rd
;
}
return
NULL
;
}
/* Add/update destinations for multicast */
static
int
vxlan_fdb_append
(
struct
vxlan_fdb
*
f
,
__be32
ip
,
__be16
port
,
__u32
vni
,
__u32
ifindex
)
{
struct
vxlan_rdst
*
rd
;
rd
=
vxlan_fdb_find_rdst
(
f
,
ip
,
port
,
vni
,
ifindex
);
if
(
rd
)
return
0
;
rd
=
kmalloc
(
sizeof
(
*
rd
),
GFP_ATOMIC
);
if
(
rd
==
NULL
)
return
-
ENOBUFS
;
...
...
@@ -388,8 +424,9 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
rd
->
remote_port
=
port
;
rd
->
remote_vni
=
vni
;
rd
->
remote_ifindex
=
ifindex
;
rd
->
remote_next
=
NULL
;
rd_prev
->
remote_next
=
rd
;
list_add_tail_rcu
(
&
rd
->
list
,
&
f
->
remotes
);
return
1
;
}
...
...
@@ -421,7 +458,8 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
notify
=
1
;
}
if
((
flags
&
NLM_F_APPEND
)
&&
is_multicast_ether_addr
(
f
->
eth_addr
))
{
(
is_multicast_ether_addr
(
f
->
eth_addr
)
||
is_zero_ether_addr
(
f
->
eth_addr
)))
{
int
rc
=
vxlan_fdb_append
(
f
,
ip
,
port
,
vni
,
ifindex
);
if
(
rc
<
0
)
...
...
@@ -441,16 +479,14 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
return
-
ENOMEM
;
notify
=
1
;
f
->
remote
.
remote_ip
=
ip
;
f
->
remote
.
remote_port
=
port
;
f
->
remote
.
remote_vni
=
vni
;
f
->
remote
.
remote_ifindex
=
ifindex
;
f
->
remote
.
remote_next
=
NULL
;
f
->
state
=
state
;
f
->
flags
=
ndm_flags
;
f
->
updated
=
f
->
used
=
jiffies
;
INIT_LIST_HEAD
(
&
f
->
remotes
);
memcpy
(
f
->
eth_addr
,
mac
,
ETH_ALEN
);
vxlan_fdb_append
(
f
,
ip
,
port
,
vni
,
ifindex
);
++
vxlan
->
addrcnt
;
hlist_add_head_rcu
(
&
f
->
hlist
,
vxlan_fdb_head
(
vxlan
,
mac
));
...
...
@@ -462,16 +498,19 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
return
0
;
}
static
void
vxlan_fdb_free_rdst
(
struct
rcu_head
*
head
)
{
struct
vxlan_rdst
*
rd
=
container_of
(
head
,
struct
vxlan_rdst
,
rcu
);
kfree
(
rd
);
}
static
void
vxlan_fdb_free
(
struct
rcu_head
*
head
)
{
struct
vxlan_fdb
*
f
=
container_of
(
head
,
struct
vxlan_fdb
,
rcu
);
struct
vxlan_rdst
*
rd
,
*
nd
;
while
(
f
->
remote
.
remote_next
)
{
struct
vxlan_rdst
*
rd
=
f
->
remote
.
remote_next
;
f
->
remote
.
remote_next
=
rd
->
remote_next
;
list_for_each_entry_safe
(
rd
,
nd
,
&
f
->
remotes
,
list
)
kfree
(
rd
);
}
kfree
(
f
);
}
...
...
@@ -487,58 +526,77 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f)
call_rcu
(
&
f
->
rcu
,
vxlan_fdb_free
);
}
/* Add static entry (via netlink) */
static
int
vxlan_fdb_add
(
struct
ndmsg
*
ndm
,
struct
nlattr
*
tb
[],
struct
net_device
*
dev
,
const
unsigned
char
*
addr
,
u16
flags
)
static
int
vxlan_fdb_parse
(
struct
nlattr
*
tb
[],
struct
vxlan_dev
*
vxlan
,
__be32
*
ip
,
__be16
*
port
,
u32
*
vni
,
u32
*
ifindex
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
net
*
net
=
dev_net
(
vxlan
->
dev
);
__be32
ip
;
__be16
port
;
u32
vni
,
ifindex
;
int
err
;
if
(
!
(
ndm
->
ndm_state
&
(
NUD_PERMANENT
|
NUD_REACHABLE
)))
{
pr_info
(
"RTM_NEWNEIGH with invalid state %#x
\n
"
,
ndm
->
ndm_state
);
return
-
EINVAL
;
}
if
(
tb
[
NDA_DST
]
==
NULL
)
return
-
EINVAL
;
if
(
nla_len
(
tb
[
NDA_DST
])
!=
sizeof
(
__be32
))
return
-
EAFNOSUPPORT
;
if
(
tb
[
NDA_DST
])
{
if
(
nla_len
(
tb
[
NDA_DST
])
!=
sizeof
(
__be32
))
return
-
EAFNOSUPPORT
;
ip
=
nla_get_be32
(
tb
[
NDA_DST
]);
*
ip
=
nla_get_be32
(
tb
[
NDA_DST
]);
}
else
{
*
ip
=
htonl
(
INADDR_ANY
);
}
if
(
tb
[
NDA_PORT
])
{
if
(
nla_len
(
tb
[
NDA_PORT
])
!=
sizeof
(
__be16
))
return
-
EINVAL
;
port
=
nla_get_be16
(
tb
[
NDA_PORT
]);
}
else
port
=
vxlan
->
dst_port
;
*
port
=
nla_get_be16
(
tb
[
NDA_PORT
]);
}
else
{
*
port
=
vxlan
->
dst_port
;
}
if
(
tb
[
NDA_VNI
])
{
if
(
nla_len
(
tb
[
NDA_VNI
])
!=
sizeof
(
u32
))
return
-
EINVAL
;
vni
=
nla_get_u32
(
tb
[
NDA_VNI
]);
}
else
vni
=
vxlan
->
default_dst
.
remote_vni
;
*
vni
=
nla_get_u32
(
tb
[
NDA_VNI
]);
}
else
{
*
vni
=
vxlan
->
default_dst
.
remote_vni
;
}
if
(
tb
[
NDA_IFINDEX
])
{
struct
net_device
*
tdev
;
if
(
nla_len
(
tb
[
NDA_IFINDEX
])
!=
sizeof
(
u32
))
return
-
EINVAL
;
ifindex
=
nla_get_u32
(
tb
[
NDA_IFINDEX
]);
tdev
=
dev_get_by_index
(
net
,
ifindex
);
*
ifindex
=
nla_get_u32
(
tb
[
NDA_IFINDEX
]);
tdev
=
dev_get_by_index
(
net
,
*
ifindex
);
if
(
!
tdev
)
return
-
EADDRNOTAVAIL
;
dev_put
(
tdev
);
}
else
ifindex
=
0
;
}
else
{
*
ifindex
=
0
;
}
return
0
;
}
/* Add static entry (via netlink) */
static
int
vxlan_fdb_add
(
struct
ndmsg
*
ndm
,
struct
nlattr
*
tb
[],
struct
net_device
*
dev
,
const
unsigned
char
*
addr
,
u16
flags
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
/* struct net *net = dev_net(vxlan->dev); */
__be32
ip
;
__be16
port
;
u32
vni
,
ifindex
;
int
err
;
if
(
!
(
ndm
->
ndm_state
&
(
NUD_PERMANENT
|
NUD_REACHABLE
)))
{
pr_info
(
"RTM_NEWNEIGH with invalid state %#x
\n
"
,
ndm
->
ndm_state
);
return
-
EINVAL
;
}
if
(
tb
[
NDA_DST
]
==
NULL
)
return
-
EINVAL
;
err
=
vxlan_fdb_parse
(
tb
,
vxlan
,
&
ip
,
&
port
,
&
vni
,
&
ifindex
);
if
(
err
)
return
err
;
spin_lock_bh
(
&
vxlan
->
hash_lock
);
err
=
vxlan_fdb_create
(
vxlan
,
addr
,
ip
,
ndm
->
ndm_state
,
flags
,
...
...
@@ -555,14 +613,43 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
vxlan_fdb
*
f
;
int
err
=
-
ENOENT
;
struct
vxlan_rdst
*
rd
=
NULL
;
__be32
ip
;
__be16
port
;
u32
vni
,
ifindex
;
int
err
;
err
=
vxlan_fdb_parse
(
tb
,
vxlan
,
&
ip
,
&
port
,
&
vni
,
&
ifindex
);
if
(
err
)
return
err
;
err
=
-
ENOENT
;
spin_lock_bh
(
&
vxlan
->
hash_lock
);
f
=
vxlan_find_mac
(
vxlan
,
addr
);
if
(
f
)
{
vxlan_fdb_destroy
(
vxlan
,
f
);
err
=
0
;
if
(
!
f
)
goto
out
;
if
(
ip
!=
htonl
(
INADDR_ANY
))
{
rd
=
vxlan_fdb_find_rdst
(
f
,
ip
,
port
,
vni
,
ifindex
);
if
(
!
rd
)
goto
out
;
}
err
=
0
;
/* remove a destination if it's not the only one on the list,
* otherwise destroy the fdb entry
*/
if
(
rd
&&
!
list_is_singular
(
&
f
->
remotes
))
{
list_del_rcu
(
&
rd
->
list
);
call_rcu
(
&
rd
->
rcu
,
vxlan_fdb_free_rdst
);
goto
out
;
}
vxlan_fdb_destroy
(
vxlan
,
f
);
out:
spin_unlock_bh
(
&
vxlan
->
hash_lock
);
return
err
;
...
...
@@ -581,23 +668,24 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
hlist_for_each_entry_rcu
(
f
,
&
vxlan
->
fdb_head
[
h
],
hlist
)
{
struct
vxlan_rdst
*
rd
;
for
(
rd
=
&
f
->
remote
;
rd
;
rd
=
rd
->
remote_next
)
{
if
(
idx
<
cb
->
args
[
0
])
goto
skip
;
if
(
idx
<
cb
->
args
[
0
])
goto
skip
;
list_for_each_entry_rcu
(
rd
,
&
f
->
remotes
,
list
)
{
err
=
vxlan_fdb_info
(
skb
,
vxlan
,
f
,
NETLINK_CB
(
cb
->
skb
).
portid
,
cb
->
nlh
->
nlmsg_seq
,
RTM_NEWNEIGH
,
NLM_F_MULTI
,
rd
);
if
(
err
<
0
)
break
;
skip:
++
idx
;
goto
out
;
}
skip:
++
idx
;
}
}
out:
return
idx
;
}
...
...
@@ -613,7 +701,9 @@ static bool vxlan_snoop(struct net_device *dev,
f
=
vxlan_find_mac
(
vxlan
,
src_mac
);
if
(
likely
(
f
))
{
if
(
likely
(
f
->
remote
.
remote_ip
==
src_ip
))
struct
vxlan_rdst
*
rdst
=
first_remote
(
f
);
if
(
likely
(
rdst
->
remote_ip
==
src_ip
))
return
false
;
/* Don't migrate static entries, drop packets */
...
...
@@ -623,10 +713,11 @@ static bool vxlan_snoop(struct net_device *dev,
if
(
net_ratelimit
())
netdev_info
(
dev
,
"%pM migrated from %pI4 to %pI4
\n
"
,
src_mac
,
&
f
->
remote
.
remote_ip
,
&
src_ip
);
src_mac
,
&
rdst
->
remote_ip
,
&
src_ip
);
f
->
remote
.
remote_ip
=
src_ip
;
rdst
->
remote_ip
=
src_ip
;
f
->
updated
=
jiffies
;
vxlan_fdb_notify
(
vxlan
,
f
,
RTM_NEWNEIGH
);
}
else
{
/* learned new entry */
spin_lock
(
&
vxlan
->
hash_lock
);
...
...
@@ -647,76 +738,61 @@ static bool vxlan_snoop(struct net_device *dev,
/* See if multicast group is already in use by other ID */
static
bool
vxlan_group_used
(
struct
vxlan_net
*
vn
,
const
struct
vxlan_dev
*
this
)
static
bool
vxlan_group_used
(
struct
vxlan_net
*
vn
,
__be32
remote_ip
)
{
struct
vxlan_dev
*
vxlan
;
list_for_each_entry
(
vxlan
,
&
vn
->
vxlan_list
,
next
)
{
if
(
vxlan
==
this
)
continue
;
if
(
!
netif_running
(
vxlan
->
dev
))
continue
;
if
(
vxlan
->
default_dst
.
remote_ip
==
this
->
default_dst
.
remote_ip
)
if
(
vxlan
->
default_dst
.
remote_ip
==
remote_ip
)
return
true
;
}
return
false
;
}
/* kernel equivalent to IP_ADD_MEMBERSHIP */
static
int
vxlan_join_group
(
struct
net_device
*
dev
)
static
void
vxlan_sock_hold
(
struct
vxlan_sock
*
vs
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
vxlan_net
*
vn
=
net_generic
(
dev_net
(
dev
),
vxlan_net_id
);
struct
sock
*
sk
=
vxlan
->
vn_sock
->
sock
->
sk
;
struct
ip_mreqn
mreq
=
{
.
imr_multiaddr
.
s_addr
=
vxlan
->
default_dst
.
remote_ip
,
.
imr_ifindex
=
vxlan
->
default_dst
.
remote_ifindex
,
};
int
err
;
atomic_inc
(
&
vs
->
refcnt
);
}
/* Already a member of group */
if
(
vxlan_group_used
(
vn
,
vxlan
))
return
0
;
static
void
vxlan_sock_release
(
struct
vxlan_net
*
vn
,
struct
vxlan_sock
*
vs
)
{
if
(
!
atomic_dec_and_test
(
&
vs
->
refcnt
))
return
;
/* Need to drop RTNL to call multicast join */
rtnl_unlock
();
lock_sock
(
sk
);
err
=
ip_mc_join_group
(
sk
,
&
mreq
);
release_sock
(
sk
);
rtnl_lock
();
spin_lock
(
&
vn
->
sock_lock
);
hlist_del_rcu
(
&
vs
->
hlist
);
spin_unlock
(
&
vn
->
sock_lock
);
return
err
;
queue_work
(
vxlan_wq
,
&
vs
->
del_work
)
;
}
/* kernel equivalent to IP_DROP_MEMBERSHIP */
static
int
vxlan_leave_group
(
struct
net_device
*
dev
)
/* Callback to update multicast group membership.
* Scheduled when vxlan goes up/down.
*/
static
void
vxlan_igmp_work
(
struct
work_struct
*
work
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
vxlan_net
*
vn
=
net_generic
(
dev_net
(
dev
),
vxlan_net_id
);
int
err
=
0
;
struct
sock
*
sk
=
v
xlan
->
vn_sock
->
sock
->
sk
;
struct
vxlan_dev
*
vxlan
=
container_of
(
work
,
struct
vxlan_dev
,
igmp_work
);
struct
vxlan_net
*
vn
=
net_generic
(
dev_net
(
vxlan
->
dev
),
vxlan_net_id
);
struct
vxlan_sock
*
vs
=
vxlan
->
vn_sock
;
struct
sock
*
sk
=
v
s
->
sock
->
sk
;
struct
ip_mreqn
mreq
=
{
.
imr_multiaddr
.
s_addr
=
vxlan
->
default_dst
.
remote_ip
,
.
imr_ifindex
=
vxlan
->
default_dst
.
remote_ifindex
,
};
/* Only leave group when last vxlan is done. */
if
(
vxlan_group_used
(
vn
,
vxlan
))
return
0
;
/* Need to drop RTNL to call multicast leave */
rtnl_unlock
();
lock_sock
(
sk
);
err
=
ip_mc_leave_group
(
sk
,
&
mreq
);
if
(
vxlan_group_used
(
vn
,
vxlan
->
default_dst
.
remote_ip
))
ip_mc_join_group
(
sk
,
&
mreq
);
else
ip_mc_leave_group
(
sk
,
&
mreq
);
release_sock
(
sk
);
rtnl_lock
();
return
err
;
vxlan_sock_release
(
vn
,
vs
);
dev_put
(
vxlan
->
dev
);
}
/* Callback from net/ipv4/udp.c to receive packets */
...
...
@@ -873,7 +949,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
}
f
=
vxlan_find_mac
(
vxlan
,
n
->
ha
);
if
(
f
&&
f
->
remote
.
remote_ip
==
htonl
(
INADDR_ANY
))
{
if
(
f
&&
f
irst_remote
(
f
)
->
remote_ip
==
htonl
(
INADDR_ANY
))
{
/* bridge-local neighbor */
neigh_release
(
n
);
goto
out
;
...
...
@@ -1015,8 +1091,8 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
}
}
static
netdev_tx_t
vxlan_xmit_one
(
struct
sk_buff
*
skb
,
struct
net_device
*
dev
,
struct
vxlan_rdst
*
rdst
,
bool
did_rsc
)
static
void
vxlan_xmit_one
(
struct
sk_buff
*
skb
,
struct
net_device
*
dev
,
struct
vxlan_rdst
*
rdst
,
bool
did_rsc
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
rtable
*
rt
;
...
...
@@ -1026,7 +1102,7 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
struct
flowi4
fl4
;
__be32
dst
;
__be16
src_port
,
dst_port
;
u32
vni
;
u32
vni
;
__be16
df
=
0
;
__u8
tos
,
ttl
;
int
err
;
...
...
@@ -1039,7 +1115,7 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if
(
did_rsc
)
{
/* short-circuited back to local bridge */
vxlan_encap_bypass
(
skb
,
vxlan
,
vxlan
);
return
NETDEV_TX_OK
;
return
;
}
goto
drop
;
}
...
...
@@ -1095,7 +1171,7 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if
(
!
dst_vxlan
)
goto
tx_error
;
vxlan_encap_bypass
(
skb
,
vxlan
,
dst_vxlan
);
return
NETDEV_TX_OK
;
return
;
}
vxh
=
(
struct
vxlanhdr
*
)
__skb_push
(
skb
,
sizeof
(
*
vxh
));
vxh
->
vx_flags
=
htonl
(
VXLAN_FLAGS
);
...
...
@@ -1123,7 +1199,7 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
IPPROTO_UDP
,
tos
,
ttl
,
df
);
iptunnel_xmit_stats
(
err
,
&
dev
->
stats
,
dev
->
tstats
);
return
NETDEV_TX_OK
;
return
;
drop:
dev
->
stats
.
tx_dropped
++
;
...
...
@@ -1133,7 +1209,6 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
dev
->
stats
.
tx_errors
++
;
tx_free:
dev_kfree_skb
(
skb
);
return
NETDEV_TX_OK
;
}
/* Transmit local packets over Vxlan
...
...
@@ -1147,9 +1222,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
ethhdr
*
eth
;
bool
did_rsc
=
false
;
struct
vxlan_rdst
*
rdst
0
,
*
rdst
;
struct
vxlan_rdst
*
rdst
;
struct
vxlan_fdb
*
f
;
int
rc1
,
rc
;
skb_reset_mac_header
(
skb
);
eth
=
eth_hdr
(
skb
);
...
...
@@ -1168,33 +1242,28 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
}
if
(
f
==
NULL
)
{
rdst0
=
&
vxlan
->
default_dst
;
if
(
rdst0
->
remote_ip
==
htonl
(
INADDR_ANY
)
&&
(
vxlan
->
flags
&
VXLAN_F_L2MISS
)
&&
!
is_multicast_ether_addr
(
eth
->
h_dest
))
vxlan_fdb_miss
(
vxlan
,
eth
->
h_dest
);
}
else
rdst0
=
&
f
->
remote
;
rc
=
NETDEV_TX_OK
;
f
=
vxlan_find_mac
(
vxlan
,
all_zeros_mac
);
if
(
f
==
NULL
)
{
if
((
vxlan
->
flags
&
VXLAN_F_L2MISS
)
&&
!
is_multicast_ether_addr
(
eth
->
h_dest
))
vxlan_fdb_miss
(
vxlan
,
eth
->
h_dest
);
dev
->
stats
.
tx_dropped
++
;
dev_kfree_skb
(
skb
);
return
NETDEV_TX_OK
;
}
}
/* if there are multiple destinations, send copies */
for
(
rdst
=
rdst0
->
remote_next
;
rdst
;
rdst
=
rdst
->
remote_next
)
{
list_for_each_entry_rcu
(
rdst
,
&
f
->
remotes
,
list
)
{
struct
sk_buff
*
skb1
;
skb1
=
skb_clone
(
skb
,
GFP_ATOMIC
);
if
(
skb1
)
{
rc1
=
vxlan_xmit_one
(
skb1
,
dev
,
rdst
,
did_rsc
);
if
(
rc
==
NETDEV_TX_OK
)
rc
=
rc1
;
}
if
(
skb1
)
vxlan_xmit_one
(
skb1
,
dev
,
rdst
,
did_rsc
);
}
rc1
=
vxlan_xmit_one
(
skb
,
dev
,
rdst0
,
did_rsc
);
if
(
rc
==
NETDEV_TX_OK
)
rc
=
rc1
;
return
rc
;
dev_kfree_skb
(
skb
);
return
NETDEV_TX_OK
;
}
/* Walk the forwarding table and purge stale entries */
...
...
@@ -1237,23 +1306,70 @@ static void vxlan_cleanup(unsigned long arg)
/* Setup stats when device is created */
static
int
vxlan_init
(
struct
net_device
*
dev
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
vxlan_net
*
vn
=
net_generic
(
dev_net
(
dev
),
vxlan_net_id
);
struct
vxlan_sock
*
vs
;
__u32
vni
=
vxlan
->
default_dst
.
remote_vni
;
dev
->
tstats
=
alloc_percpu
(
struct
pcpu_tstats
);
if
(
!
dev
->
tstats
)
return
-
ENOMEM
;
spin_lock
(
&
vn
->
sock_lock
);
vs
=
vxlan_find_port
(
dev_net
(
dev
),
vxlan
->
dst_port
);
if
(
vs
)
{
/* If we have a socket with same port already, reuse it */
atomic_inc
(
&
vs
->
refcnt
);
vxlan
->
vn_sock
=
vs
;
hlist_add_head_rcu
(
&
vxlan
->
hlist
,
vni_head
(
vs
,
vni
));
}
else
{
/* otherwise make new socket outside of RTNL */
dev_hold
(
dev
);
queue_work
(
vxlan_wq
,
&
vxlan
->
sock_work
);
}
spin_unlock
(
&
vn
->
sock_lock
);
return
0
;
}
static
void
vxlan_fdb_delete_defualt
(
struct
vxlan_dev
*
vxlan
)
{
struct
vxlan_fdb
*
f
;
spin_lock_bh
(
&
vxlan
->
hash_lock
);
f
=
__vxlan_find_mac
(
vxlan
,
all_zeros_mac
);
if
(
f
)
vxlan_fdb_destroy
(
vxlan
,
f
);
spin_unlock_bh
(
&
vxlan
->
hash_lock
);
}
static
void
vxlan_uninit
(
struct
net_device
*
dev
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
vxlan_net
*
vn
=
net_generic
(
dev_net
(
dev
),
vxlan_net_id
);
struct
vxlan_sock
*
vs
=
vxlan
->
vn_sock
;
vxlan_fdb_delete_defualt
(
vxlan
);
if
(
vs
)
vxlan_sock_release
(
vn
,
vs
);
free_percpu
(
dev
->
tstats
);
}
/* Start ageing timer and join group when device is brought up */
static
int
vxlan_open
(
struct
net_device
*
dev
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
int
err
;
struct
vxlan_sock
*
vs
=
vxlan
->
vn_sock
;
/* socket hasn't been created */
if
(
!
vs
)
return
-
ENOTCONN
;
if
(
IN_MULTICAST
(
ntohl
(
vxlan
->
default_dst
.
remote_ip
)))
{
err
=
vxlan_join_group
(
dev
);
if
(
err
)
return
err
;
vxlan_sock_hold
(
vs
);
dev_hold
(
dev
);
queue_work
(
vxlan_wq
,
&
vxlan
->
igmp_work
)
;
}
if
(
vxlan
->
age_interval
)
...
...
@@ -1273,7 +1389,9 @@ static void vxlan_flush(struct vxlan_dev *vxlan)
hlist_for_each_safe
(
p
,
n
,
&
vxlan
->
fdb_head
[
h
])
{
struct
vxlan_fdb
*
f
=
container_of
(
p
,
struct
vxlan_fdb
,
hlist
);
vxlan_fdb_destroy
(
vxlan
,
f
);
/* the all_zeros_mac entry is deleted at vxlan_uninit */
if
(
!
is_zero_ether_addr
(
f
->
eth_addr
))
vxlan_fdb_destroy
(
vxlan
,
f
);
}
}
spin_unlock_bh
(
&
vxlan
->
hash_lock
);
...
...
@@ -1283,9 +1401,13 @@ static void vxlan_flush(struct vxlan_dev *vxlan)
static
int
vxlan_stop
(
struct
net_device
*
dev
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
vxlan_sock
*
vs
=
vxlan
->
vn_sock
;
if
(
IN_MULTICAST
(
ntohl
(
vxlan
->
default_dst
.
remote_ip
)))
vxlan_leave_group
(
dev
);
if
(
vs
&&
IN_MULTICAST
(
ntohl
(
vxlan
->
default_dst
.
remote_ip
)))
{
vxlan_sock_hold
(
vs
);
dev_hold
(
dev
);
queue_work
(
vxlan_wq
,
&
vxlan
->
igmp_work
);
}
del_timer_sync
(
&
vxlan
->
age_timer
);
...
...
@@ -1301,6 +1423,7 @@ static void vxlan_set_multicast_list(struct net_device *dev)
static
const
struct
net_device_ops
vxlan_netdev_ops
=
{
.
ndo_init
=
vxlan_init
,
.
ndo_uninit
=
vxlan_uninit
,
.
ndo_open
=
vxlan_open
,
.
ndo_stop
=
vxlan_stop
,
.
ndo_start_xmit
=
vxlan_xmit
,
...
...
@@ -1319,12 +1442,6 @@ static struct device_type vxlan_type = {
.
name
=
"vxlan"
,
};
static
void
vxlan_free
(
struct
net_device
*
dev
)
{
free_percpu
(
dev
->
tstats
);
free_netdev
(
dev
);
}
/* Initialize the device structure. */
static
void
vxlan_setup
(
struct
net_device
*
dev
)
{
...
...
@@ -1337,7 +1454,7 @@ static void vxlan_setup(struct net_device *dev)
dev
->
hard_header_len
=
ETH_HLEN
+
VXLAN_HEADROOM
;
dev
->
netdev_ops
=
&
vxlan_netdev_ops
;
dev
->
destructor
=
vxlan_free
;
dev
->
destructor
=
free_netdev
;
SET_NETDEV_DEVTYPE
(
dev
,
&
vxlan_type
);
dev
->
tx_queue_len
=
0
;
...
...
@@ -1354,6 +1471,8 @@ static void vxlan_setup(struct net_device *dev)
INIT_LIST_HEAD
(
&
vxlan
->
next
);
spin_lock_init
(
&
vxlan
->
hash_lock
);
INIT_WORK
(
&
vxlan
->
igmp_work
,
vxlan_igmp_work
);
INIT_WORK
(
&
vxlan
->
sock_work
,
vxlan_sock_work
);
init_timer_deferrable
(
&
vxlan
->
age_timer
);
vxlan
->
age_timer
.
function
=
vxlan_cleanup
;
...
...
@@ -1445,7 +1564,6 @@ static void vxlan_del_work(struct work_struct *work)
kfree_rcu
(
vs
,
rcu
);
}
/* Create new listen socket if needed */
static
struct
vxlan_sock
*
vxlan_socket_create
(
struct
net
*
net
,
__be16
port
)
{
struct
vxlan_sock
*
vs
;
...
...
@@ -1453,6 +1571,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port)
struct
sockaddr_in
vxlan_addr
=
{
.
sin_family
=
AF_INET
,
.
sin_addr
.
s_addr
=
htonl
(
INADDR_ANY
),
.
sin_port
=
port
,
};
int
rc
;
unsigned
int
h
;
...
...
@@ -1478,8 +1597,6 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port)
sk
=
vs
->
sock
->
sk
;
sk_change_net
(
sk
,
net
);
vxlan_addr
.
sin_port
=
port
;
rc
=
kernel_bind
(
vs
->
sock
,
(
struct
sockaddr
*
)
&
vxlan_addr
,
sizeof
(
vxlan_addr
));
if
(
rc
<
0
)
{
...
...
@@ -1497,18 +1614,57 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port)
udp_sk
(
sk
)
->
encap_type
=
1
;
udp_sk
(
sk
)
->
encap_rcv
=
vxlan_udp_encap_recv
;
udp_encap_enable
();
atomic_set
(
&
vs
->
refcnt
,
1
);
vs
->
refcnt
=
1
;
return
vs
;
}
/* Scheduled at device creation to bind to a socket */
static
void
vxlan_sock_work
(
struct
work_struct
*
work
)
{
struct
vxlan_dev
*
vxlan
=
container_of
(
work
,
struct
vxlan_dev
,
sock_work
);
struct
net_device
*
dev
=
vxlan
->
dev
;
struct
net
*
net
=
dev_net
(
dev
);
__u32
vni
=
vxlan
->
default_dst
.
remote_vni
;
__be16
port
=
vxlan
->
dst_port
;
struct
vxlan_net
*
vn
=
net_generic
(
net
,
vxlan_net_id
);
struct
vxlan_sock
*
nvs
,
*
ovs
;
nvs
=
vxlan_socket_create
(
net
,
port
);
if
(
IS_ERR
(
nvs
))
{
netdev_err
(
vxlan
->
dev
,
"Can not create UDP socket, %ld
\n
"
,
PTR_ERR
(
nvs
));
goto
out
;
}
spin_lock
(
&
vn
->
sock_lock
);
/* Look again to see if can reuse socket */
ovs
=
vxlan_find_port
(
net
,
port
);
if
(
ovs
)
{
atomic_inc
(
&
ovs
->
refcnt
);
vxlan
->
vn_sock
=
ovs
;
hlist_add_head_rcu
(
&
vxlan
->
hlist
,
vni_head
(
ovs
,
vni
));
spin_unlock
(
&
vn
->
sock_lock
);
sk_release_kernel
(
nvs
->
sock
->
sk
);
kfree
(
nvs
);
}
else
{
vxlan
->
vn_sock
=
nvs
;
hlist_add_head_rcu
(
&
nvs
->
hlist
,
vs_head
(
net
,
port
));
hlist_add_head_rcu
(
&
vxlan
->
hlist
,
vni_head
(
nvs
,
vni
));
spin_unlock
(
&
vn
->
sock_lock
);
}
out:
dev_put
(
dev
);
}
static
int
vxlan_newlink
(
struct
net
*
net
,
struct
net_device
*
dev
,
struct
nlattr
*
tb
[],
struct
nlattr
*
data
[])
{
struct
vxlan_net
*
vn
=
net_generic
(
net
,
vxlan_net_id
);
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
vxlan_rdst
*
dst
=
&
vxlan
->
default_dst
;
struct
vxlan_sock
*
vs
;
__u32
vni
;
int
err
;
...
...
@@ -1586,36 +1742,25 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
return
-
EEXIST
;
}
vs
=
vxlan_find_port
(
net
,
vxlan
->
dst_port
);
if
(
vs
)
++
vs
->
refcnt
;
else
{
/* Drop lock because socket create acquires RTNL lock */
rtnl_unlock
();
vs
=
vxlan_socket_create
(
net
,
vxlan
->
dst_port
);
rtnl_lock
();
if
(
IS_ERR
(
vs
))
return
PTR_ERR
(
vs
);
hlist_add_head_rcu
(
&
vs
->
hlist
,
vs_head
(
net
,
vxlan
->
dst_port
));
}
vxlan
->
vn_sock
=
vs
;
SET_ETHTOOL_OPS
(
dev
,
&
vxlan_ethtool_ops
);
/* create an fdb entry for default destination */
err
=
vxlan_fdb_create
(
vxlan
,
all_zeros_mac
,
vxlan
->
default_dst
.
remote_ip
,
NUD_REACHABLE
|
NUD_PERMANENT
,
NLM_F_EXCL
|
NLM_F_CREATE
,
vxlan
->
dst_port
,
vxlan
->
default_dst
.
remote_vni
,
vxlan
->
default_dst
.
remote_ifindex
,
NTF_SELF
);
if
(
err
)
return
err
;
err
=
register_netdevice
(
dev
);
if
(
err
)
{
if
(
--
vs
->
refcnt
==
0
)
{
rtnl_unlock
();
sk_release_kernel
(
vs
->
sock
->
sk
);
kfree
(
vs
);
rtnl_lock
();
}
vxlan_fdb_delete_defualt
(
vxlan
);
return
err
;
}
list_add
(
&
vxlan
->
next
,
&
vn
->
vxlan_list
);
hlist_add_head_rcu
(
&
vxlan
->
hlist
,
vni_head
(
vs
,
vni
));
return
0
;
}
...
...
@@ -1623,16 +1768,10 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
static
void
vxlan_dellink
(
struct
net_device
*
dev
,
struct
list_head
*
head
)
{
struct
vxlan_dev
*
vxlan
=
netdev_priv
(
dev
);
struct
vxlan_sock
*
vs
=
vxlan
->
vn_sock
;
hlist_del_rcu
(
&
vxlan
->
hlist
);
list_del
(
&
vxlan
->
next
);
unregister_netdevice_queue
(
dev
,
head
);
if
(
--
vs
->
refcnt
==
0
)
{
hlist_del_rcu
(
&
vs
->
hlist
);
schedule_work
(
&
vs
->
del_work
);
}
}
static
size_t
vxlan_get_size
(
const
struct
net_device
*
dev
)
...
...
@@ -1721,6 +1860,7 @@ static __net_init int vxlan_init_net(struct net *net)
unsigned
int
h
;
INIT_LIST_HEAD
(
&
vn
->
vxlan_list
);
spin_lock_init
(
&
vn
->
sock_lock
);
for
(
h
=
0
;
h
<
PORT_HASH_SIZE
;
++
h
)
INIT_HLIST_HEAD
(
&
vn
->
sock_list
[
h
]);
...
...
@@ -1750,6 +1890,10 @@ static int __init vxlan_init_module(void)
{
int
rc
;
vxlan_wq
=
alloc_workqueue
(
"vxlan"
,
0
,
0
);
if
(
!
vxlan_wq
)
return
-
ENOMEM
;
get_random_bytes
(
&
vxlan_salt
,
sizeof
(
vxlan_salt
));
rc
=
register_pernet_device
(
&
vxlan_net_ops
);
...
...
@@ -1765,14 +1909,16 @@ static int __init vxlan_init_module(void)
out2:
unregister_pernet_device
(
&
vxlan_net_ops
);
out1:
destroy_workqueue
(
vxlan_wq
);
return
rc
;
}
late_initcall
(
vxlan_init_module
);
static
void
__exit
vxlan_cleanup_module
(
void
)
{
rtnl_link_unregister
(
&
vxlan_link_ops
);
unregister_pernet_device
(
&
vxlan_net_ops
);
rtnl_link_unregister
(
&
vxlan_link_ops
);
destroy_workqueue
(
vxlan_wq
);
rcu_barrier
();
}
module_exit
(
vxlan_cleanup_module
);
...
...
net/bridge/br_fdb.c
View file @
3f5d6af0
...
...
@@ -707,6 +707,11 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
}
}
if
(
is_zero_ether_addr
(
addr
))
{
pr_info
(
"bridge: RTM_NEWNEIGH with invalid ether address
\n
"
);
return
-
EINVAL
;
}
p
=
br_port_get_rtnl
(
dev
);
if
(
p
==
NULL
)
{
pr_info
(
"bridge: RTM_NEWNEIGH %s not a bridge port
\n
"
,
...
...
net/core/rtnetlink.c
View file @
3f5d6af0
...
...
@@ -2109,10 +2109,6 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
}
addr
=
nla_data
(
tb
[
NDA_LLADDR
]);
if
(
is_zero_ether_addr
(
addr
))
{
pr_info
(
"PF_BRIDGE: RTM_NEWNEIGH with invalid ether address
\n
"
);
return
-
EINVAL
;
}
err
=
-
EOPNOTSUPP
;
...
...
@@ -2210,10 +2206,6 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
}
addr
=
nla_data
(
tb
[
NDA_LLADDR
]);
if
(
is_zero_ether_addr
(
addr
))
{
pr_info
(
"PF_BRIDGE: RTM_DELNEIGH with invalid ether address
\n
"
);
return
-
EINVAL
;
}
err
=
-
EOPNOTSUPP
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment