How to find I/F to destination
Subsystems (2)
Hi,
I would like to determine through which interface packets sent to a particular UDP destination will go through, and so determine the MTU size for that interface. Can anyone suggest a good way of doing this from within the kernel?
Doing this will permit AF_RXRPC to obtain a better initial guess as to the maximum size of the packets that can be sent that way.
I could use the code Patrick gave me to allow AFS to iterate through all the interfaces and then pick the smallest MTU, but that seems wrong somehow - though it probably will result in the correct answer 99% of the time.
David
Quoted 2 lines · David Howells
Hi,
Hi David.
Quoted 4 lines
I would like to determine through which interface packets sent to a particular UDP destination will go through, and so determine the MTU size for that interface. Can anyone suggest a good way of doing this from within the kernel?
I used following code in netchannels:
static int netchannel_ip_route_output_flow(struct rtable **rp, struct flowi *flp, int flags)
{
int err;
err = __ip_route_output_key(rp, flp);
if (err)
return err;
if (flp->proto) {
if (!flp->fl4_src)
flp->fl4_src = (*rp)->rt_src;
if (!flp->fl4_dst)
flp->fl4_dst = (*rp)->rt_dst;
}
return 0;
}struct dst_entry *route_get_raw(u32 saddr, u32 daddr, u16 sport, u16 dport, u8 proto)
{
struct rtable *rt;
struct flowi fl = { .oif = 0,
.nl_u = { .ip4_u =
{ .saddr = saddr,
.daddr = daddr,
.tos = 0 } },
.proto = proto,
.uli_u = { .ports =
{ .sport = sport,
.dport = dport } } };
if (netchannel_ip_route_output_flow(&rt, &fl, 0))
goto no_route;
return dst_clone(&rt->u.dst);no_route:
return NULL; }
This is basically a copied input route code.
dst entry, obtained from route_get_raw() holds a pointer to network device.
Signature · 1 line
Evgeniy Polyakov
Quoted 1 line · Evgeniy Polyakov
err = __ip_route_output_key(rp, flp);
Is there any way to get at this without having to link against the ipv4 module?
David
Quoted 4 lines · David Howells
Quoted 1 line
err = __ip_route_output_key(rp, flp);Is there any way to get at this without having to link against the ipv4 module?
Ah, nevermind. ipv4 can't be a module.
David
Should route_get_raw() release the rtable if gets back?
David
Quoted 2 lines · David Howells
Should
route_get_raw()release the rtable if gets back?
Yes, dst entry should be released when not used anymore.
Quoted 1 line
David
Signature · 1 line
Evgeniy Polyakov
Quoted 3 lines · Evgeniy Polyakov
Quoted 1 line
Should
route_get_raw()release the rtable if gets back?Yes, dst entry should be released when not used anymore.
I meant the rtable returned by __ip_route_output_key(), not the dst.
David
Quoted 7 lines · David Howells
Quoted 3 lines · Evgeniy Polyakov
Quoted 1 line
Should
route_get_raw()release the rtable if gets back?Yes, dst entry should be released when not used anymore.
I meant the rtable returned by
__ip_route_output_key(), not the dst.
That is the same, dst is dereferenced as rtable.
Cloned dst is returned, so it must be put back at the ned of the usage.
Quoted 1 line
David
Signature · 1 line
Evgeniy Polyakov
Quoted 2 lines · Evgeniy Polyakov
That is the same, dst is dereferenced as rtable.
Cloned dst is returned, so it must be put back at the ned of the usage.
If that's the case, then why do you bother to clone it in route_get_raw()? Surely that'll give you *two* clones...
BTW, it seems to work. The attached function gives me:
[0mount ] <== rxrpc_assess_MTU_size() [if_mtu 1500]
Thanks!
David
---
static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer)
{
struct rtable *rt;
struct flowi fl;
int ret;
peer->if_mtu = 1500;
memset(&fl, 0, sizeof(fl));
switch (peer->srx.transport.family) {
case AF_INET:
fl.oif = 0;
fl.proto = IPPROTO_UDP,
fl.nl_u.ip4_u.saddr = 0;
fl.nl_u.ip4_u.daddr = peer->srx.transport.sin.sin_addr.s_addr;
fl.nl_u.ip4_u.tos = 0;
/* assume AFS.CM talking to AFS.FS */
fl.uli_u.ports.sport = htonl(7001);
fl.uli_u.ports.dport = htonl(7000);
break;
default:
BUG();
}
ret = ip_route_output_key(&rt, &fl);
if (ret < 0) {
kleave(" [route err %d]", ret);
return;
}
peer->if_mtu = dst_mtu(&rt->u.dst);
kleave(" [if_mtu %u]", peer->if_mtu);
}Quoted 12 lines · David Howells
static void
rxrpc_assess_MTU_size(struct rxrpc_peer *peer){ struct rtable *rt; struct flowi fl; int ret; peer->if_mtu = 1500; memset(&fl, 0, sizeof(fl)); switch (peer->srx.transport.family) { case AF_INET:
Quoted 8 lines
fl.oif = 0; fl.proto = IPPROTO_UDP, fl.nl_u.ip4_u.saddr = 0; fl.nl_u.ip4_u.daddr = peer->srx.transport.sin.sin_addr.s_addr; fl.nl_u.ip4_u.tos = 0; /* assume AFS.CM talking to AFS.FS */ fl.uli_u.ports.sport = htonl(7001); fl.uli_u.ports.dport = htonl(7000);
htons()
Quoted 5 lines
break; default: BUG(); }
Quoted 7 lines
ret = ip_route_output_key(&rt, &fl);
if (ret < 0) {
kleave(" [route err %d]", ret);
return;
}
peer->if_mtu = dst_mtu(&rt->u.dst);You need dst_release(&rt->u.dst) here.
Quoted 1 line · Patrick McHardy
htons()
Blech. Thanks. Does it actually matter what ports are specified?
Quoted 1 line
You need
dst_release(&rt->u.dst)here.
Thanks. I think Evgeniy's code may be wrong then. He ends with a dst_clone(), which I think is superfluous.
David
Quoted 7 lines · David Howells
Quoted 1 line · Patrick McHardy
htons()Blech. Thanks. Does it actually matter what ports are specified?
It matters when IPsec port selectors are used to find the correct policy.
Quoted 5 lines
Quoted 1 line
You need
dst_release(&rt->u.dst)here.Thanks. I think Evgeniy's code may be wrong then. He ends with a
dst_clone(), which I think is superfluous.
Yes, that looks wrong.
Quoted 8 lines · Patrick McHardy
Quoted 5 lines
Quoted 1 line
You need
dst_release(&rt->u.dst)here.Thanks. I think Evgeniy's code may be wrong then. He ends with a
dst_clone(), which I think is superfluous.Yes, that looks wrong.
Main idea is to get a reference, and then clone it for each user. Then each user drops its reference, and when system is not used anymore, main reference is dropped too.
Signature · 1 line
Evgeniy Polyakov
Quoted 35 lines · David Howells
Quoted 2 lines · Evgeniy Polyakov
That is the same, dst is dereferenced as rtable.
Cloned dst is returned, so it must be put back at the ned of the usage.If that's the case, then why do you bother to clone it in
route_get_raw()? Surely that'll give you *two* clones...BTW, it seems to work. The attached function gives me:
[0mount ] <== rxrpc_assess_MTU_size() [if_mtu 1500]Thanks!
David
---
static voidrxrpc_assess_MTU_size(struct rxrpc_peer *peer){ struct rtable *rt; struct flowi fl; int ret; peer->if_mtu = 1500; memset(&fl, 0, sizeof(fl)); switch (peer->srx.transport.family) { case AF_INET: fl.oif = 0; fl.proto = IPPROTO_UDP, fl.nl_u.ip4_u.saddr = 0; fl.nl_u.ip4_u.daddr = peer->srx.transport.sin.sin_addr.s_addr; fl.nl_u.ip4_u.tos = 0; /* assume AFS.CM talking to AFS.FS */
Quoted 8 lines
fl.uli_u.ports.sport = htonl(7001); fl.uli_u.ports.dport = htonl(7000); break; default: BUG(); } ret = ip_route_output_key(&rt, &fl);
This one is quite slow compared to atomic reference increase, so yes, there are two clones - one for main 'route', which then is being cloned for each used in fast path. When it is not needed anymore (netchannel is removed), the first one is dropped.
Quoted 8 lines
if (ret < 0) {
kleave(" [route err %d]", ret);
return;
}
peer->if_mtu = dst_mtu(&rt->u.dst);
kleave(" [if_mtu %u]", peer->if_mtu);
}Signature · 1 line
Evgeniy Polyakov
Quoted 2 lines · David Howells
Should
route_get_raw()release the rtable if gets back?
You can also cache returned entry and then just clone it and check ->obsolete() callback.
Something like this:
struct dst_entry *route_get(struct dst_entry *dst)
{
if (dst && dst->obsolete && dst->ops->check(dst, 0) == NULL) {
dst_release(dst);
return NULL;
}
return dst_clone(dst);
}Copied from route code too.
Quoted 1 line
David
Signature · 1 line
Evgeniy Polyakov
Quoted 1 line · Evgeniy Polyakov
static int
netchannel_ip_route_output_flow(struct rtable **rp, struct flowi *flp, int flags)
What's proto? Should that be IPPROTO_UDP?
David
Quoted 5 lines · David Howells
Quoted 1 line · Evgeniy Polyakov
static int
netchannel_ip_route_output_flow(struct rtable **rp, struct flowi *flp, int flags)What's proto? Should that be
IPPROTO_UDP?
Yep.
Quoted 1 line
David
Signature · 1 line
Evgeniy Polyakov
Quoted 4 lines · David Howells
I would like to determine through which interface packets sent to a particular UDP destination will go through, and so determine the MTU size for that interface. Can anyone suggest a good way of doing this from within the kernel?
Do a route lookup (ip_route_output_key), then either use dst_mtu to get the PMTU value or dst->dev in case you really want the device's MTU.