multiple physical interfaces on same subnet

David Ball dball at zetron.com
Fri Mar 28 05:54:04 UTC 2008


Hi RTEMS Users,

 

I was having the same problem that Alex Zolotov had in message
http://www.rtems.com/ml/rtems-users/2008/january/msg00062.html regarding
multiple interfaces in the same subnet. I can't find a reply or solution
to his post.

 

We have 2 physical interfaces on the same subnet (for redundancy not
load sharing).

eth0 192.168.65.32 255.255.255.0

eth1 192.168.65.33 255.255.255.0

 

Eth0 inits fine but then the following errors messages are displayed:

Can't set eth1 address: File exists

Can't set eth2 address: File exists

(Which corresponds to EEXIST.)

 

The error message immediately comes from:

rtems_glue.c: rtems_bsdnet_setup() Code attached below

and further down

route.c: rtrequest() Code attached below

 

I think it is when BSDnet is trying to add the following entries to the
route table:

192.168.65.0    255.255.255.0      U           0        0     14 eth0

 

It should be noted that the latest BSDnet code in in.c
(http://www.freebsd.org/cgi/cvsweb.cgi/~checkout~/src/sys/netinet/in.c?r
ev=1.102.2.1;content-type=text%2Fplain) differs in the function
in_ifinit() from even the latest RTEMS code. Tt has a new function
in_addprefix() which searches for existing entries in the route table
and the does nothing if it is a duplicate. This function is called in
place of rtinit() but will call rtinit() if no route entry has been
found.
 
See latest RTEMS in.c : in_ifinit() Code attached below
 
See latest bsdnet (Note that I modified it slightly to work with RTEMS)
Code attached below
 
This *patch* allows bsdnet to successful initialize without errors. 
 
However, this causes the next problem. It seems all packets are sent
from the eth0 interface. This has been tested in the following ways:
* Ping from an external host to 192.168.65.34 (eth1) only returns when
the eth0 cable is plugged in
* The replies to the ping (when the cable is in) from eth1 have the eth0
MAC address

* Any packets generated by the card always have eth0 MAC address (eg
open a socket and bind to the eth1 address and then send ... packets
have the eth1 ip but have the eth0 MAC)

 

Is this behavior as expected? I think so as only eth0 will have a route.

 

So I wrote a quick custom patch for bsdnet ip_output.c ip_output() which
searches through the interface addresses and tries to match the packet
header ip src and address. Then it 'corrects' the ia and ifp pointers.

 

      /*

       * If routing to interface only,

       * short circuit routing lookup.

       */

#define ifatoia(ifa)    ((struct in_ifaddr *)(ifa))

#define sintosa(sin)    ((struct sockaddr *)(sin))

      if (flags & IP_ROUTETOIF) {

            if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == 0 &&

                (ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == 0) {

                  ipstat.ips_noroute++;

                  error = ENETUNREACH;

                  goto bad;

            }

            ifp = ia->ia_ifp;

            ip->ip_ttl = 1;

            isbroadcast = in_broadcast(dst->sin_addr, ifp);

      } else {

            /*

             * If this is the case, we probably don't want to allocate

             * a protocol-cloned route since we didn't get one from the

             * ULP.  This lets TCP do its thing, while not burdening

             * forwarding or ICMP with the overhead of cloning a route.

             * Of course, we still want to do any cloning requested by

             * the link layer, as this is probably required in all cases

             * for correct operation (as it is for ARP).

             */

            if (ro->ro_rt == 0)

                  rtalloc_ign(ro, RTF_PRCLONING);

            if (ro->ro_rt == 0) {

                  ipstat.ips_noroute++;

                  error = EHOSTUNREACH;

                  goto bad;

            }

            ia = ifatoia(ro->ro_rt->rt_ifa);

            ifp = ro->ro_rt->rt_ifp;

-- patch start --

        /* dball: Note custom patch here

         * Use the user ip src address to search through the interfaces

         * to find the 'right' interface to send from.

         */

        struct in_ifaddr *ia_search = ia;

        do {

            if (Bcmp((void *) &ip->ip_src,

                (void *) &IA_SIN(ia_search)->sin_addr,

                IA_SIN(ia_search)->sin_len) == 0) {

                ia = ia_search;

                ifp = ia_search->ia_ifp;

                    break;

                }

            } while ( (ia_search = ia_search->ia_next) != 0);

-- patch end --

            ro->ro_rt->rt_use++;

            if (ro->ro_rt->rt_flags & RTF_GATEWAY)

                  dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway;

            if (ro->ro_rt->rt_flags & RTF_HOST)

                  isbroadcast = (ro->ro_rt->rt_flags & RTF_BROADCAST);

            else

                  isbroadcast = in_broadcast(dst->sin_addr, ifp);

      }

 

This makes it work as I want with the packets been sent from the correct
physical interfaces and having the correct MAC addresses. (Note that I
know it isn't a complete solution, eg I really should search for all
addresses attached to each interface)

 

What do you think of this patch?

 

I am on RTEMS 4.7 (with many imported patches), PowerPC BSP.

 

Regards,

David Ball

 

 

rtems_glue.c: rtems_bsdnet_setup()

**

            /*

             * Set interface address

             */

            memset (&address, '\0', sizeof address);

            address.sin_len = sizeof address;

            address.sin_family = AF_INET;

            address.sin_addr.s_addr = inet_addr (ifp->ip_address);

            if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFADDR, &address)
< 0) {

                  printf ("Can't set %s address: %s\n", ifp->name,
strerror (errno));

                  continue;

            }

**

 

route.c: rtrequest

**

            /*

             * This moved from below so that rnh->rnh_addaddr() can

             * examine the ifa and ifp if it so desires.

             */

            ifa->ifa_refcnt++;

            rt->rt_ifa = ifa;

            rt->rt_ifp = ifa->ifa_ifp;

 

            rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask,

                              rnh, rt->rt_nodes);

            if (rn == 0) {

                  struct rtentry *rt2;

                  /*

                   * Uh-oh, we already have one of these in the tree.

                   * We do a special hack: if the route that's already

                   * there was generated by the protocol-cloning

                   * mechanism, then we just blow it away and retry

                   * the insertion of the new one.

                   */

                  rt2 = rtalloc1(dst, 0, RTF_PRCLONING);

                  if (rt2 && rt2->rt_parent) {

                        rtrequest(RTM_DELETE, 

                                (struct sockaddr *)rt_key(rt2),

                                rt2->rt_gateway,

                                rt_mask(rt2), rt2->rt_flags, 0);

                        RTFREE(rt2);

                        rn = rnh->rnh_addaddr((caddr_t)ndst,

                                          (caddr_t)netmask,

                                          rnh, rt->rt_nodes);

                  } else if (rt2) {

                        RTFREE(rt2);

                  }

            }

 

            if (rn == 0) {

                  if (rt->rt_gwroute)

                        rtfree(rt->rt_gwroute);

                  if (rt->rt_ifa) {

                        IFAFREE(rt->rt_ifa);

                  }

                  Free(rt_key(rt));

                  Free(rt);

                  senderr(EEXIST);

            }

**

 

RTEMS in.c : in_ifinit()
        /*
         * Add route for the network.
         */
        ia->ia_ifa.ifa_metric = ifp->if_metric;
        if (ifp->if_flags & IFF_BROADCAST) {
               ia->ia_broadaddr.sin_addr.s_addr =
                       htonl(ia->ia_subnet | ~ia->ia_subnetmask);
               ia->ia_netbroadcast.s_addr =
                       htonl(ia->ia_net | ~ ia->ia_netmask);
        } else if (ifp->if_flags & IFF_LOOPBACK) {
               ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
               flags |= RTF_HOST;
        } else if (ifp->if_flags & IFF_POINTOPOINT) {
               if (ia->ia_dstaddr.sin_family != AF_INET)
                       return (0);
               flags |= RTF_HOST;
        }
        if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags)) == 0)
               ia->ia_flags |= IFA_ROUTE;

 

 

in.c in_ifinit() (Latest bsdnet (Note that I modified it slightly to
work with RTEMS))

**

        /*
         * Add route for the network.
         */
        ia->ia_ifa.ifa_metric = ifp->if_metric;
        if (ifp->if_flags & IFF_BROADCAST) {
               ia->ia_broadaddr.sin_addr.s_addr =
                       htonl(ia->ia_subnet | ~ia->ia_subnetmask);
               ia->ia_netbroadcast.s_addr =
                       htonl(ia->ia_net | ~ ia->ia_netmask);
        } else if (ifp->if_flags & IFF_LOOPBACK) {
               ia->ia_dstaddr = ia->ia_addr;
               flags |= RTF_HOST;
        } else if (ifp->if_flags & IFF_POINTOPOINT) {
               if (ia->ia_dstaddr.sin_family != AF_INET)
                       return (0);
               flags |= RTF_HOST;
        }
        if ((error = in_addprefix(ia, flags)) != 0)
               return (error);
 
--- snip ---
 
#define rtinitflags(x) \
        ((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) !=
0) \
            ? RTF_HOST : 0)
/*
 * Check if we have a route for the given prefix already or add one
accordingly.
 */
static int
in_addprefix(struct in_ifaddr *target, int flags)
{
        struct in_ifaddr *ia;
        struct in_addr prefix, mask, p, m;
        int error;
 
        if ((flags & RTF_HOST) != 0) {
               prefix = target->ia_dstaddr.sin_addr;
               mask.s_addr = 0;
        } else {
               prefix = target->ia_addr.sin_addr;
               mask = target->ia_sockmask.sin_addr;
               prefix.s_addr &= mask.s_addr;
        }
 
        for (ia = in_ifaddr; ia; ia = ia->ia_next) {
               if (rtinitflags(ia)) {
                       p = ia->ia_addr.sin_addr;
 
                       if (prefix.s_addr != p.s_addr)
                               continue;
               } else {
                       p = ia->ia_addr.sin_addr;
                       m = ia->ia_sockmask.sin_addr;
                       p.s_addr &= m.s_addr;
 
                       if (prefix.s_addr != p.s_addr ||
                           mask.s_addr != m.s_addr)
                               continue;
               }
 
               /*
                * If we got a matching prefix route inserted by other
                * interface address, we are done here.
                */
               if (ia->ia_flags & IFA_ROUTE) {
                       return (0);
               }
        }
 
        /*
         * No-one seem to have this prefix route, so we try to insert
it.
         */
        error = rtinit(&target->ia_ifa, (int)RTM_ADD, flags);
        if (!error)
               target->ia_flags |= IFA_ROUTE;
        return error;
}

**

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.rtems.org/pipermail/users/attachments/20080327/213c747c/attachment.html>


More information about the users mailing list