Go to main content

Oracle® Solaris 11.3 Programming Interfaces Guide

Exit Print View

Updated: April 2019
 
 

Using the Packet Filtering Hooks Interfaces

A substantial amount of programming is required to work with the packet filtering hooks interfaces because this API supports multiple instances of the IP stack running concurrently in the same kernel. The IP stack allows multiple instances of itself for zones and multiple instances of the framework support packet interception in IP.

This section demonstrates the use of packet filtering hooks API to receive inbound IPv4 packets.

IP Instances

When using packet filtering hooks API, decide whether to accommodate multiple instances of IP running in the kernel or to only interact with the global zone.

To know if the IP instances are present, register callback functions that are activated when an instance is created, destroyed, and shut down. Use the net_instance_alloc() function to allocate a net_instance_t packet event structure to store the function pointers. Use net_instance_free() to free resources when you no longer need the callbacks and the structure. Specify nin_name to give the structure instance a name. The nin_create() function is called when a new instance of IP is created, and the nin_destroy() function is called when an instance of IP is destroyed. Specify at least the nin_create() and nin_destroy() callbacks.

Specifying nin_shutdown() is optional unless the code will be exporting information to kstats. To use kstats on a per-instance basis, use net_kstat_create() during the create callback. The kstat information should cleaned up during the shutdown callback and not the destroy callback. Use net_kstat_delete() to clean up kstat information.

extern void *mycreate(const netid_t);

net_instance_t *n;

n = net_instance_alloc(NETINFO_VERSION);
if (n != NULL) {
    n->nin_create = mycreate;
    n->nin_destroy = mydestroy;
    n->nin_name = "my module";
    if (net_instance_register(n) != 0)
        net_instance_free(n);
}

If one or more instances of IP are present when net_instance_alloc() is called, the create callback will be called for each currently active instance. The framework that supports the callbacks ensures that only one of the create, destroy, or shutdown functions is active at any one time for a given instance. The framework also ensures that once the create callback has been called, the shutdown callback will only be called after create has completed. Similarly, the destroy callback does not start until the shutdown callback is complete.

The mycreate() function in the following example shows how to a create callback. The mycreate() function records the network instance identifier in its own private context structure and registers a new callback to be called when a new protocol such as IPv4 or IPv6 is registered with the framework.

If no zones are running (and therefore no instances other than the global zone), calling net_instance_register() runs the create callback for the global zone. You must supply the destroy callback so that net_instance_unregister() can be called later. Attempts to call net_instance_register() with either the nin_create or nin_destroy fields set to NULL will fail.

void *
mycreate(const netid_t id)
{
    mytype_t *ctx;

    ctx = kmem_alloc(sizeof(*ctx), KM_SLEEP);
    ctx->instance_id = id;
    net_instance_notify_register(id, mynewproto, ctx);
    return (ctx);
}

The function mynewproto() will be called each time a network protocol is either added to or removed from a networking instance. If registered network protocols are already operating within the given instance, then the create callback will be called for each protocol that exists.

Protocol Registration

For the mynewproto() callback, the caller fills in only the proto argument. Neither an event nor a hook name can be provided at this point. The following example looks for only the events that announce the registration of the IPv4 protocol.

The next step in this function is to discover when events are added to the IPv4 protocol by using the net_protocol_notify_register() interface to register the mynewevent() function.

static int
mynewproto(hook_notify_cmd_t cmd, void *arg, const char *proto,
    const char *event, const char *hook)
{
    mytype_t *ctx = arg;

    if (strcmp(proto, NHF_INET) != 0)
        return (0);

    switch (cmd) {
        case HN_REGISTER :
            ctx->inet = net_protocol_lookup(s->id, proto);
            net_protocol_notify_register(s->inet, mynewevent, ctx);
            break;
        case HN_UNREGISTER :
        case HN_NONE :
            break;
    }
    return (0);
}

The following table lists all three protocols that could be expected to be seen with the mynewproto() callback. New protocols could be added in the future, so you must safely fail (return the value 0) any unknown protocols.

Programming Symbol
Protocol
NHF_INET
IPv4
NHF_INET6
IPv6
NHF_ARP
ARP

Event Registration

Just as the handling of instances and protocols is dynamic, the handling of the events that live under each protocol also is dynamic. Two types of events are supported by this API: network interface events and packet events.

In the following function, the announcement for the presence of the event for inbound packets for IPv4 is being checked for. When that announcement is seen, a hook_t structure is allocated, describing the function to be called for each inbound IPv4 packet.

static int
mynewevent(hook_notify_cmd_t cmd, void *arg, const char *parent,
    const char *event, const char *hook)
{
    mytype_t *ctx = arg;
    char buffer[32];
    hook_t *h;

    if ((strcmp(event, NH_PHYSICAL_IN) == 0) &&
        (strcmp(parent, NHF_INET) == 0)) {
            snprintf(buffer, 

sizeof(buffer), "mypkthook_%s_%s", parent, event);
            h = hook_alloc(HOOK_VERSION);
            h->h_hint = HH_NONE;
            h->h_arg = s;
            h->h_name = strdup(buffer);
            h->h_func = mypkthook;
            s->hook_in = h;
            net_hook_register(ctx->inet, (char *)event, h);
    } else {
            h = NULL;
    }
    return (0);
}

The function mynewevent() will be called for each event that is added and removed. The following events are available.

Event Name
Data Structure
Comment
NH_PHYSICAL_IN
hook_pkt_event_t
This event is generated for every packet that arrives at the network protocol and has been received from a network interface driver.
NH_PHYSICAL_OUT
hook_pkt_event_t
This event is generated for every packet prior to delivery to the network interface driver for sending from the network protocol layer.
NH_FORWARDING
hook_pkt_event_t
This event is for all packets that have been received by the system and will be sent out another network interface. This event happens after NH_PHYSICAL_IN and before NH_PHYSICAL_OUT.
NH_LOOPBACK_IN
hook_pkt_event_t
This event is generated for packets that are received on the loopback interface or that are received by a zone that is sharing its network instance with the global zone.
NH_LOOPBACK_OUT
hook_pkt_event_t
This event is generated for packets that are sent on the loopback interface or that are being sent by a zone that is sharing its network instance with the global zone.
NH_NIC_EVENTS
hook_nic_event_t
This event is generated for specific changes of state for network interfaces.

For packet events, there is one specific event for each particular point in the IP stack. This is to enable you to be selective about exactly where in the flow of the packets you wish to intercept packets, without being overburdened by examining every packet event that happens inside the kernel. For network interface events the model is different, in part because the events are much lower in volume and because it is more likely that the developer will be interested in several of them, not just one.

    The network interface event announces one of the following events:

  • An interface is created (NE_PLUMB) or destroyed (NE_UNPLUMB).

  • An interface changes state to up (NE_UP) or down (NE_DOWN).

  • An interface has an address change (NE_ADDRESS_CHANGE).

New network interface events could be added in the future, so you must always return 0 for any unknown or unrecognized event that the callback function receives.

Packet Hook Function

The packet hook function is called when a packet is received. In this case, the function mypkthook() can be called for each inbound packet that arrives in the kernel from a physical network interface.

To illustrate the difference between accepting a packet and allowing the function to return normally with what is required to drop a packet, the following code prints the source and destination address of every 100th packet and then drops the packet, introducing a packet loss of 1%.

static int
mypkthook(hook_event_token_t tok, hook_data_t data, void *arg)
{
    static int counter = 0;
    mytupe_t *ctx = arg;
    hook_pkt_event_t *pkt = (hook_pkt_event_t)data;
    struct ip *ip;
    size_t bytes;

    bytes = msgdsize(pkt->hpe_mb);

    ip = (struct ip *)pkt->hpe_hdr;

    counter++;
    if (counter == 100) {
        printf("drop %d bytes received from %x to %x\n", bytes,
            ntohl(ip->ip_src.s_addr), ntohl(ip->ip_dst.s_addr));
        counter = 0;
        freemsg(*pkt->hpe_mp);
        *pkt->hpe_mp = NULL;
        pkt->hpe_mb = NULL;
        pkt->hpe_hdr = NULL;
        return (1);
    }
    return (0);
}

Packets received by this function, and all others that are called as a callback from a packet event, are received one at a time. There is no chaining together of packets with this interface, so you should expect only one packet per call and expect b_next to always be NULL. While there is no other packet, a single packet may be comprised of several mblk_t structures chained together with b_cont.