In this series I present following improvements and fixes:
1) Forbid "type" parameter when parsing command line
for peer in iplink_vxcan.c and link_veth.c using
iplink_parse(): we already known it.
2) In ll_remember_index() update ifname, not only rehash
it. It might be changed for same ifindex since last
run (i.e. in cache "eth0" during the dump "ppp0").
3) Make ifname fixed size array of chars in @struct ll_cache:
names are never exceed IFNAMSIZ (16 bytes). Replace
strcmp()/strcpy() with memcmp()/memcpy() to possibly
benefit from compiler call inlining.
See individual patch description message for details.
Thanks,
Serhii
Serhey Popovych (3):
vxcan,veth: Forbid "type" for peer device
utils: ll_map: Update name and type for existing entry
utils: ll_map: Make network device name fixed size array of char
ip/iplink_vxcan.c | 3 +++
ip/link_veth.c | 3 +++
lib/ll_map.c | 47 +++++++++++++++++++++++++++--------------------
3 files changed, 33 insertions(+), 20 deletions(-)
--
1.7.10.4
It is already given for original device we configure this
peer for.
Results from following command before/after change applied
are shown below:
$ ip link add dev veth1a type veth peer name veth1b \
type veth peer name veth1c
Before:
-------
<no output, no netdevs created>
After:
------
Error: argument "type" is wrong: not supported for peer
Signed-off-by: Serhey Popovych <redacted>
---
ip/iplink_vxcan.c | 3 +++
ip/link_veth.c | 3 +++
2 files changed, 6 insertions(+)
In case of we update existing entry we need not only rehash
but also update name in existing entry.
Need to update device type too since cached interface might
be deleted and new with same index, but different type
added (e.g. eth0 and ppp0).
Reuse new entry initialization path to avoid duplications.
Signed-off-by: Serhey Popovych <redacted>
---
lib/ll_map.c | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
Network device names are fixed in size and never exceed
IFNAMSIZ (16 bytes).
Make name fixed size array to always malloc() same size chunk
of memory and use memcpy()/memcmp() with constant IFNAMSIZ
to benefit from possible compiler optimizations replacing
call to a function with two/four load/store instructions
on 64/32 bit systems.
Check if IFLA_IFNAME attribute present in netlink message
(should always) and use strncpy() to pad name with zeros.
Signed-off-by: Serhey Popovych <redacted>
---
lib/ll_map.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
From: Stephen Hemminger <stephen@networkplumber.org> Date: 2017-12-26 17:05:39
On Wed, 20 Dec 2017 09:37:29 +0200
Serhey Popovych [off-list ref] wrote:
It is already given for original device we configure this
peer for.
Results from following command before/after change applied
are shown below:
$ ip link add dev veth1a type veth peer name veth1b \
type veth peer name veth1c
Before:
-------
<no output, no netdevs created>
After:
------
Error: argument "type" is wrong: not supported for peer
Signed-off-by: Serhey Popovych <redacted>
This makes a lot of sense.
Maybe the better error message would be to use duparg() since
that is the most likely cause of the user error.
On Wed, 20 Dec 2017 09:37:29 +0200
Serhey Popovych [off-list ref] wrote:
quoted
It is already given for original device we configure this
peer for.
Results from following command before/after change applied
are shown below:
$ ip link add dev veth1a type veth peer name veth1b \
type veth peer name veth1c
Before:
-------
<no output, no netdevs created>
After:
------
Error: argument "type" is wrong: not supported for peer
Signed-off-by: Serhey Popovych <redacted>
This makes a lot of sense.
Maybe the better error message would be to use duparg() since
that is the most likely cause of the user error.
Not sure Stephen. With duparg() I get following error message for
test case described in above.
Error: duplicate "type": "veth" is the second value.
It looks good for me, but we have two "type veth" in command and
which one is wrong might not be very clear to user.
Anyway I send v2 for this particular change to get and option
to pick the best one.
Thanks.
It is already given for original device we configure this
peer for.
Results from following command before/after change applied
are shown below:
$ ip link add dev veth1a type veth peer name veth1b \
type veth peer name veth1c
Before:
-------
<no output, no netdevs created>
After:
------
Error: duplicate "type": "veth" is the second value.
Signed-off-by: Serhey Popovych <redacted>
---
ip/iplink_vxcan.c | 3 +++
ip/link_veth.c | 3 +++
2 files changed, 6 insertions(+)
From: Stephen Hemminger <stephen@networkplumber.org> Date: 2017-12-28 18:11:13
On Thu, 28 Dec 2017 13:01:04 +0200
Serhey Popovych [off-list ref] wrote:
It is already given for original device we configure this
peer for.
Results from following command before/after change applied
are shown below:
$ ip link add dev veth1a type veth peer name veth1b \
type veth peer name veth1c
Before:
-------
<no output, no netdevs created>
After:
------
Error: duplicate "type": "veth" is the second value.
Signed-off-by: Serhey Popovych <redacted>
Applied this one. The other util patches have some issues
On Wed, 20 Dec 2017 09:37:31 +0200
Serhey Popovych [off-list ref] wrote:
quoted
Network device names are fixed in size and never exceed
IFNAMSIZ (16 bytes).
Make name fixed size array to always malloc() same size chunk
of memory and use memcpy()/memcmp() with constant IFNAMSIZ
to benefit from possible compiler optimizations replacing
call to a function with two/four load/store instructions
on 64/32 bit systems.
Check if IFLA_IFNAME attribute present in netlink message
(should always) and use strncpy() to pad name with zeros.
Signed-off-by: Serhey Popovych <redacted>
---
lib/ll_map.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
@@ -82,7 +82,7 @@ int ll_remember_index(const struct sockaddr_nl *who,structnlmsghdr*n,void*arg){unsignedinth;-constchar*ifname;+charifname[IFNAMSIZ];structifinfomsg*ifi=NLMSG_DATA(n);structll_cache*im;structrtattr*tb[IFLA_MAX+1];
@@ -105,17 +105,21 @@ int ll_remember_index(const struct sockaddr_nl *who,}parse_rtattr(tb,IFLA_MAX,IFLA_RTA(ifi),IFLA_PAYLOAD(n));-ifname=rta_getattr_str(tb[IFLA_IFNAME]);-if(ifname==NULL)++if(!tb[IFLA_IFNAME])+return0;+strncpy(ifname,rta_getattr_str(tb[IFLA_IFNAME]),IFNAMSIZ);+if(!ifname[0])return0;+ifname[IFNAMSIZ-1]='\0';if(im){/* change to existing entry */-rehash=strcmp(im->name,ifname);+rehash=memcmp(im->name,ifname,IFNAMSIZ);
This is not safe. There is not guarantee that bytes after end of string are zero.
Sorry Stephen, correct if my assumptions are wrong:
1. struct ll_cache entries are only modified in ll_remember_index().
There are no places where we may modify ll_cache entries.
2. strncpy() always pad with zeroes to the end of IFNAMSIZ sized
buffer.
3. strncpy() may not return null terminated string: this addressed
with ifname[IFNAMSIZ - 1] = '\0' in the code above.
Assuming 1 and 2 we always have im->name[] initialized with string and
zero pads up to IFNAMSIZ. We prepare ifname using strncpy() to, so it
is zero padded and we can safely use memcmp() to compare byte by byte.
And in your code, strncpy() will overwrite characters from the beginning to null,
it will not overwrite after that. Then comparison with entry may not work because
of the data after that.
strncpy() will not pad with zeroes up to IFNAMSIZ? I get from strncpy(3)
it will pad to the end of buf, so IFNAMSIZ is initialized.
Please correct me if I'm wrong at some point.
Thanks.
I really doubt this is critical path on anything. Probably just having a better
hash table implementation would solve that.
Well this is critical with thousands of interfaces. I guess. Didn't go
with tests, but can do that. Of course difference might not be so big as
I expect, but anyway.
From: Stephen Hemminger <stephen@networkplumber.org> Date: 2017-12-28 18:18:40
On Wed, 20 Dec 2017 09:37:31 +0200
Serhey Popovych [off-list ref] wrote:
quoted hunk
Network device names are fixed in size and never exceed
IFNAMSIZ (16 bytes).
Make name fixed size array to always malloc() same size chunk
of memory and use memcpy()/memcmp() with constant IFNAMSIZ
to benefit from possible compiler optimizations replacing
call to a function with two/four load/store instructions
on 64/32 bit systems.
Check if IFLA_IFNAME attribute present in netlink message
(should always) and use strncpy() to pad name with zeros.
Signed-off-by: Serhey Popovych <redacted>
---
lib/ll_map.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
@@ -82,7 +82,7 @@ int ll_remember_index(const struct sockaddr_nl *who,structnlmsghdr*n,void*arg){unsignedinth;-constchar*ifname;+charifname[IFNAMSIZ];structifinfomsg*ifi=NLMSG_DATA(n);structll_cache*im;structrtattr*tb[IFLA_MAX+1];
@@ -105,17 +105,21 @@ int ll_remember_index(const struct sockaddr_nl *who,}parse_rtattr(tb,IFLA_MAX,IFLA_RTA(ifi),IFLA_PAYLOAD(n));-ifname=rta_getattr_str(tb[IFLA_IFNAME]);-if(ifname==NULL)++if(!tb[IFLA_IFNAME])+return0;+strncpy(ifname,rta_getattr_str(tb[IFLA_IFNAME]),IFNAMSIZ);+if(!ifname[0])return0;+ifname[IFNAMSIZ-1]='\0';if(im){/* change to existing entry */-rehash=strcmp(im->name,ifname);+rehash=memcmp(im->name,ifname,IFNAMSIZ);
This is not safe. There is not guarantee that bytes after end of string are zero.
And in your code, strncpy() will overwrite characters from the beginning to null,
it will not overwrite after that. Then comparison with entry may not work because
of the data after that.
I really doubt this is critical path on anything. Probably just having a better
hash table implementation would solve that.
From: Stephen Hemminger <stephen@networkplumber.org> Date: 2017-12-28 18:20:07
On Wed, 20 Dec 2017 09:37:30 +0200
Serhey Popovych [off-list ref] wrote:
In case of we update existing entry we need not only rehash
but also update name in existing entry.
Need to update device type too since cached interface might
be deleted and new with same index, but different type
added (e.g. eth0 and ppp0).
Reuse new entry initialization path to avoid duplications.
Signed-off-by: Serhey Popovych <redacted>
Can you provide an example where this is an observed bug?
I suspect that unless you use a batch mode command the reload
of the cache on next invocation is solving this.
On Wed, 20 Dec 2017 09:37:30 +0200
Serhey Popovych [off-list ref] wrote:
quoted
In case of we update existing entry we need not only rehash
but also update name in existing entry.
Need to update device type too since cached interface might
be deleted and new with same index, but different type
added (e.g. eth0 and ppp0).
Reuse new entry initialization path to avoid duplications.
Signed-off-by: Serhey Popovych <redacted>
Can you provide an example where this is an observed bug?
I suspect that unless you use a batch mode command the reload
of the cache on next invocation is solving this.
From my side example from description is pretty clear: eth0 -> ppp0
or rename eth0 -> eth1, etc.
According to ll_remember_index() code we might be called with
non-empty cache. If ll_get_by_index() returns an entry with
name that differs from current we need:
1. Rehash in ->name_hash (done with current code)
2. Update entry name (not done with current code)
That's my point of view.
Thanks.