On Sun, Jul 14, 2019 at 05:07:33PM +0300, Vadim Penzin wrote:
> Please find below a patch that contains the following:
> 
> (a) An explanation of alignment of address structures within a routing
> message;
> 
> (b) An example (I admit, it is somewhat lengthy --- 146 lines) of a basic
> parser of route(4) messages.
> 
> (This message is a follow-up to
> https://marc.info/?l=openbsd-tech&m=155627978527725)
> 
> Index: route.4
> ===================================================================
> RCS file: /cvs/src/share/man/man4/route.4,v
> retrieving revision 1.52
> diff -u -p -r1.52 route.4
> --- route.4   30 Apr 2019 16:34:19 -0000      1.52
> +++ route.4   14 Jul 2019 13:39:33 -0000
> @@ -137,13 +137,13 @@ routing information for all address fami
>  to a specific address family by specifying which one is desired.
>  There can be more than one routing socket open per system.
>  .Pp
> -Messages are formed by a header followed by a small
> +A routing message consists of a header that may be followed by a
>  number of
>  .Vt sockaddr
> -structures (which are variable length),
> -interpreted by position, and delimited
> -by the length entry in the
> -.Vt sockaddr .
> +structures of a variable size (the size of a particular structure
> +is determined by its
> +.Vt sa_len
> +entry; every structure is aligned on the boundary of sizeof(long)).
>  An example of a message with four addresses might be an
>  IPv4 route addition: the destination, netmask, gateway, and label,
>  since both netmasks and labels are sent over the routing socket as
> @@ -477,6 +477,119 @@ Specifiers for which addresses are prese
>  #define RTA_SRC              0x100   /* source sockaddr present */
>  #define RTA_SRCMASK  0x200   /* source netmask present */
>  #define RTA_LABEL    0x400   /* route label present */
> +.Ed
> +.Sh EXAMPLES
> +Parsing a routing message.
> +.Bd -literal
> +#include <sys/socket.h>
> +#include <net/if.h>
> +#include <net/route.h>
> +#include <stdint.h>
> +#include <err.h>
> +
> +/* An abstraction of a routing message. */
> +union rt_msg {
> +     struct rt_msghdr rt;
> +     struct if_msghdr ifm;
> +     struct ifa_msghdr ifa;
> +     struct if_announcemsghdr ifan;
> +     uint8_t buf [ RTM_MAXSIZE ];
> +};
> +
> +#define rt_msglen rt.rtm_msglen
> +#define rt_version rt.rtm_version
> +#define rt_type rt.rtm_type
> +#define rt_hdrlen rt.rtm_hdrlen
> +#define rt_buf buf
> +
> +/* Round up the size of a struct sockaddr as required by route(4). */
> +#define ROUNDUP(x) (1 + (((x) - 1) | (sizeof(long) - 1)))
> +
> +/* Parse address structures that may follow the header. */
> +
> +static union rt_msg *
> +rt_msg_parse_addrs (union rt_msg * const msg, const int addrs)
> +{
> +     const void *p = (void *) msg + msg->rt_hdrlen;
> +     const void * const q  = (void *) msg + msg->rt_msglen;
> +     int mask = addrs;
> +
> +     while (p < q && mask ) {
> +             const struct sockaddr * const sa = p;
> +             const int rta = mask ^ (mask & mask - 1);
> +
> +             /*
> +              * Here the variable `sa' points to the current sockadr
> +              * and the variable `rta' holds the corresponding
> +              * RTA_ value.
> +              *
> +              * NOTE: Before using these variables, you MUST validate
> +              * that (a) the value of `rta' is defined and expected;
> +              * (b) `sa_len' of `sa' must correspond to its `sa_family'.
> +              */
> +
> +             p += ROUNDUP (sa->sa_len);
> +             mask &= ~rta;
> +     }
> +
> +     if (mask)
> +             warn ("A route(4) message is missing (some) addresses.");
> +
> +     if (p < q)
> +             warn ("A route(4) message contains odd data.");
> +
> +     if (p > q) {
> +             err (4, "A route(4) message is malformed.");
> +             return NULL;
> +     }
> +
> +     return msg;
> +}
> +
> +/* Parse a message. */
> +
> +static union rt_msg *
> +rt_msg_parse ( union rt_msg * const msg )
> +{
> +     int addrs = 0;
> +
> +     if (msg -> rt_version != RTM_VERSION ) {
> +             err (4, "Unexpected version of a route(4) message: %d",
> +                     msg->rt_version);
> +             return NULL;
> +     }
> +
> +     switch (msg -> rt_type) {
> +     case RTM_ADD:
> +     case RTM_DELETE:
> +     case RTM_CHANGE:
> +     case RTM_GET:
> +     case RTM_LOSING:
> +     case RTM_REDIRECT:
> +     case RTM_MISS:
> +     case RTM_RESOLVE:
> +     case RTM_DESYNC:
> +             addrs = msg->rt.rtm_addrs;
> +             break;
> +     case RTM_NEWADDR:
> +     case RTM_DELADDR:
> +             addrs = msg->ifa.ifam_addrs;
> +             break;
> +     case RTM_IFINFO:
> +             addrs = msg->ifm.ifm_addrs;
> +             break;
> +     case RTM_IFANNOUNCE:
> +             return msg;
> +     case RTM_INVALIDATE:
> +             err (4, "RTM_INVALIDATE received from route(4).");
> +             return NULL;
> +     default:
> +             warn ("Unknown route(4) message: %d", msg->rt_type );
> +             return NULL;
> +     }
> +
> +     return rt_msg_parse_addrs (msg, addrs);
> +}

I don't like this example. It is not in the style of any rtsock parser we
have in tree. rt_msg_parse_addrs() is to complex to easily understand.
There are too many blank spots in the example that need to be filled in to
make it actully usable. In my opinion doing a proper example would go
beyond the scope of the man page.

-- 
:wq Claudio

Reply via email to