sun.com docs.sun.com My Sun Worldwide Sites

  Previous Contents Index Next
Chapter 9

Packet Filtering Hooks

Packet filtering hooks facilitate packet filtering between zones. Packet filtering uses a new interface inside the Solaris kernel. Other modules in the kernel interact with network layer traffic through this interface.

Packet Filtering Architecture

Packet filtering hooks use a new interface for the kernel, netinfo(9f). This interface is intended for access to network-layer protocols.

The Solaris operating system supports multiple implementations of IP within the system at any given time. The netinfo interface provides a generic interface that enables other implementations of both IP protocols.

The netinfo interface contains the following components:

  • Network interface information

  • Network protocol configuration

  • Notification of events concerning network interfaces

  • Packet interception

  • Packet transmission

The Solaris operating system distinguishes between physical and logical interfaces. The ifconfig(1M) command returns a list of logical interfaces with names that match the driver that the system uses to interact with the physical interface. The main packet filtering interface makes it possible to match up packets with physical interfaces, other interface properties, such as addresses, require an additional reference to a logical interface.

Network Interface Information

Each network interface on Solaris is associated with a collection of information that relates to addresses as well as the interface's state.

Network Addresses

A logical interface is associated with the following address data:

  • Network and host address, with netmask and prefix length available separately.

  • Broadcast address for broadcast links.

  • Peer address for point to point links.

Non-address Information

Network interfaces are also associated with the following non-address information:

  • Maximum transmission unit size (MTU).

  • Network interface state (up or down).

  • Interface name.

Each network interface also has an identifier that describes a packet's ingress or egress interface.

Network Interface Events

The details that are associated with a network interface can change over the course of its life. To keep track of what these changes are, we provide a mechanism by which a kernel module can register a callback function. The callback function is invoked each time a change event occurs. The system generates a change event when one of the following changes occurs in the IP stack:

  • A physical network interface is attached.

  • A physical network interface is detached.

  • A logical network interface is created.

  • A logical network interface is destroyed.

  • A network address is changed on a logical interface.

  • A logical network interface is enabled.

  • A logical network interface is disabled.

Network Interface Event Concurrency

Because multiple events can activate simultaneously, the handling function for the callbacks must be MP-safe.

Packet Injection

Packet filtering in the Solaris operating system supports three different modes of packet delivery for fully constructed packets:

  • Direct transmission of packets.

  • Queueing packets for input as if they had been received on a network interface.

  • Queueing packets for output as if they had been generated by the operating system.

The net_inject(9f) function adds any required layer 2 information to the packet and places the resulting message on the outbound queue for that network interface.

The task queue function taskq(9f) enables delayed packet handling.

Network Protocol Interface

A new kernel module called netinfo provides access to networking information in the kernel. Network protocols that publish their functionality and modules that access networking information must link to the netinfo module.

Network protocols must register with the netinfo framework by calling the net_register() function to make their functionality available. This function takes a pointer to a net_info_t structure as an argument. The net_info_t structure contains a set of pointers to functions that implement the netinfo interface and fields that describe the structure's version and protocol name. The net_register() function returns a token value that must be passed back to the net_unregister() function. If the net_register() function is called during a module's loading phase, call the net_unregister() function as the module unloads. If there are outstanding references to the protocol, the call to the net_unregister() function will fail with an EBUSY error. When the call to the net_unregister() function fails, the calling module must fail the unload request.

The net_lookup() function provides access to the netinfo repository. This function returns a token value that is passed back through all subsequent interactions with the netinfo interface. The return value from this function can be safely cached for use until you call the net_release(). You can use the net_walk() function to enumerate all of the registered network protocols. The value that the net_walk() function returns is only valid until it has been passed on to a subsequent call.

Each implementation of a network protocol provides a set of events. These events are provided asynchronously and can also be delivered simultaneously. This simultaneity applies to all events, regardless of type. To provide an event, call the net_register_event() with the return value from a successful call to the net_register() and a hook_event_t structure. You can remove a registered event that has no current users by calling the net_unregister_event() with the same parameters that you used for the call to the net_register_event() function.

Network Interfaces

You can call the net_phygetnext() function to discover which physical network interfaces are available through a given network protocol. Indicate the start of the walk by passing 0 as the second argument. When the net_phygetnext() function returns 0, the walk is complete. You can search for a physical network interface by passing the name of the interface to the net_phylookup() function.

The net_phygetnext() and net_phylookup() functions both return a value called phy_int_t. You can use this value with other functions in this interface to learn about the interface's properties.

The current queries and set operations supported with a physical interface are:

  • Retrieving the name of the interface and storing it in a buffer using the net_getifname() function.

  • Returning the MTU value for the interface using the net_getmtu() function.

  • Returning the status of packet forwarding using the net_getforwarding() function.

  • Setting the behavior of packet forwarding using the net_setforwarding() function. If the value of the phy_int_t argument is 0, the change applies to all of the network interfaces.

To retrieve network address information, you must first acquire a handle for a logical interface by calling the net_lifgetnext() function. This function operates in the same way as the net_phygetnext() function, but uses the phy_int_t argument for context.

Packet interception only associates with the physical network interface. For zones, this means that if a packet is assigned to an IP address on interface hme0:2, the packet is seen as associated with the interface hme0.

You can use the net_getlifaddr() function to return one or more components of the network address information that is associated with the logical interface.

Use the net_routeto() function to determine the interface that the operating system uses to send a packet out to a specific destination. The net_inject() function can send packets out of an interface directly or queue packets for system processing.

Packet Interception

Call the net_register_hook() function to register a function that intercepts packets. This function fails if another function is currently registered for packet interception. To remove a function's registration, call the net_unregister_hook() function with arguments matching the arguments in the original call.

When the registered callback function executes, it uses a structure that contains the following information:

  • A value that indicates which interface the packet has been received on for inbound packets. A value of 0 indicates no relevant interface.

  • A value that indicates which interface the packet is being sent out on for outbound packets. A value of 0 indicates no relevant interface.

  • A token that passes back into the netinfo framework. The token indicates which protocol the packet belongs to.

  • A pointer to the start of the network protocol header.

  • The length of the network protocol header.

  • A pointer to the mblk that contains the start of the network protocol header.

  • A pointer to the start of the mblk chain that contains the packet.

The list of events for which packets may be intercepted is as follows:

NH_PRE_ROUTING

External packets that have been received by a network protocol but have not yet been processed for either local delivery or forwarding.

NH_POST_ROUTING

Packets that have been passed through the routing infrastructure and are transmitted externally.

NH_FORWARDING

Packets that have been received from an external source and are destined for an address connected to one of its other network interfaces.

NH_LOOPBACK_IN

The receiving side of packets that have been sent by the system to an address that corresponds to one of its logical interfaces.

NH_LOOPBACK_OUT

The sending side of packets that are being sent internally to the system. The destination address for these packets corresponds to one of the system's logical interfaces.

If a packet is intercepted while being forwarded, the callback function takes a value for both the received and intended outbound interface.

Network Interface Events

NE_PLUMB

This event is generated when the configuration of a network protocol adds a network interface.

NE_UNPLUMB

This event is generated when the configuration of a network protocol removes a network interface.

NE_UP

This event is generated when the status of a network interface changes to up, indicating that it can now be used to send and receive packets.

NE_DOWN

This event is generated when the status of a network interface changes to down, indicating that it is no longer receiving or generating packets.

NE_ADDRESS_CHANGE

This event is generated when the address that is associated with a logical interface changes.

Callback Entitlements

Any processing that you do as part of the packet interception callback must not block. Treat programming callbacks in the same way as executing interrupt contexts for a driver. For example, a callback cannot call the kmem_alloc(9F) function with the KM_SLEEP flag. A callback cannot interface with user space through the use of the copyin(9F), copyout(9F), or other similar functions.


Note - A callback cannot make any function calls into the netinfo interface that involve the callback part of the interface. A callback may not remove itself or add another callback while it is executing. A callback cannot call the net_register_event(), net_unregister_event(), net_register_hook(), or net_unregister_hook() functions.


Checksums

Due to the location of the interception points for packets in IPv4 and IPv6, the callbacks for these protocols may see incomplete checksums. For packets that are generated by the system, interfaces that support hardware checksums may compute a partial checksum for TCP and UDP.

When the TCP/UDP checksum is offloaded, adjust the checksums rather than recompute them if the callback modifies the network or transport layer header. This adjustment must be in line with changes to the header fields. If the data portion of the packet also changes, the callback must call the net_ispartialchecksum() function to discover if the system uses partial checksums for this packet. If the net_ispartialchecksum() function returns a value of TRUE, do not update the headers. A value of FALSE requires a full checksum recalculation.

To determine if the checksums in a packet's header are correct, the callback calls the net_isvalidcksum() function. This function's return value of TRUE or FALSE indicates whether or not the network and transport headers are correct.

Code Examples

This section presents several code examples to show the structures and functions that are available for packet filtering.

Packet Filtering Structures

typedef uintptr_t phy_if_t;
typedef uintptr_t net_if_t;


typedef struct net_info {
        int neti_version = 1;
        char *neti_protocol;
        int (*neti_getforwarding)(phy_if_t ifp);
        int (*neti_setforwarding)(phy_if_t ifp, int onoff);
        int (*neti_getifname)(phy_if_t ifp, net_if_t lif,
                              char *buffer,
                              const size_t buflen);
        int (*neti_getmtu)(phy_if_t ifp, net_if_t lif);
        int (*neti_getpmtuenabled)(void);
        int (*neti_getlifaddr)(phy_if_t ifp, net_if_t lif,
                               size_t nelem,
                               net_ifaddr_t type[],
                               struct sockaddr storage[]);
        phy_if_t (*neti_phygetnext)(phy_if_t ifp);
        phy_if_t (*neti_phylookup)(const char *name);
        net_if_t (*neti_lifgetnext)(net_if_t lif);
        int (*neti_inject)(inject_t, net_inject_t *);
        phy_if_t (*neti_routeto)(struct sockaddr *address);
        int (*neti_ispartialchecksum)(mblk_t *mb);
        int (*neti_isvalidchecksum)(mblk_t *mb);
} net_info_t;


typedef enum net_ifaddr {
      NA_ADDRESS = 1,
      NA_PEER,
      NA_BROADCAST,
      NA_NETMASK
} net_ifaddr_t;


typedef enum inject {
      NI_QUEUE_IN = 1,
      NI_QUEUE_OUT,
      NI_DIRECT_OUT
} inject_t;


typedef struct net_inject {
      mblk_t *ni_packet;
      phy_if_t ni_physical;
      net_if_t ni_logical;
} net_inject_t;


#define HOOK_VERSION 1


typedef void *net_hook_t;
typedef void *net_event_t;


typedef int (*hook_func_t)(net_event_t token, hook_data_t info);

typedef struct hook {
      int32_t     h_version;  /* Currently set to 1 */
      hook_func_t h_func;     /* Pointer to callback */
      char        *h_name;    /* Name of this hook */
      int         h_flags;    /* Extra hook properties */
} hook_t;


typedef struct hook_event {
      int32_t   he_version;    /* Currently set to 1 */
      char      *he_name;      /* Name of this hook list */
      int       he_flags;      /* 1 = multiple entries allowed /
      boolean_t he_interested; /* true if registered callback exists */
} hook_event_t;


typedef void *net_data_t;
typedef void *nic_event_data_t;


typedef enum nic_event {
      NE_PLUMB = 1,
      NE_UNPLUMB,
      NE_UP,
      NE_DOWN,
      NE_ADDRESS_CHANGE,
} nic_event_t;


typedef struct hook_nic_event {
      net_data_t hne_family;
      phy_if_t hne_nic;
      lif_if_t hne_lif;
      nic_event_t hne_event;
      nic_event_data_t hne_data;
      size_t hne_datalen;
} hook_nic_event_t;


typedef struct hook_pkt_event {
      phy_if_t hpe_ifp;
      phy_if_t hpe_oip;
      void *hpe_hdr;
      mblk_t **hpe_mp;
      mblk_t *hpe_mb;
} hook_pkt_event_t;

Packet Filtering Events

This section gives some details on the events available in the packet filtering interface.

Network Events
/*


* Names of network events available through netinfo with this project.
*/
#define NH_PRE_ROUTING PRE_ROUTING
#define NH_FORWARDING FORWARDING
#define NH_POST_ROUTING POST_ROUTING
#define NH_LOOPBACK_IN LOOPBACK_IN
#define NH_LOOPBACK_OUT LOOPBACK_OUT
#define NH_NIC_EVENTS NIC_EVENTS
Packet Events

When a callback occurs due to one of the packet events in the following table, a pointer to a hook_pkt_event structure is passed in as the hook_data_info_t field. The table below describes the validity of the fields in the hook_pkt_event structure. The last row of this table indicates which fields made be changed by a callback and which fields may not be changed.

Event

hpe_ifp

hpe_ofp

hpe_hdr

hpe_mp

hpe_mb

NH_PRE_ROUTING

Yes

No

Yes

Yes

Yes

NH_POST_ROUTING

No

Yes

Yes

Yes

Yes

NH_FORWARDING

Yes

Yes

Yes

Yes

Yes

NH_LOOPBACK_IN

Yes

No

Yes

Yes

Yes

NH_LOOPBACK_OUT

No

Yes

Yes

Yes

Yes

Allows to Change

No

No

Yes

Yes

Yes

Network Interface Events

In each of these events, the fields in the nic_event_t structure are set as follows:

  • The hne_family field contains a valid reference for a network protocol and matches the value that is returned from the net_lookup() or net_walk() functions.

  • The hne_nic field represents the physical network interface to which an event belongs.

  • The hne_event field is set to a value in the first column.

Each event has the following field behavior:

NE_PLUMB

-

NE_UNPLUMB

-

NE_UP

-

NE_DOWN

-

NE_ADDRESS_CHANGE

The hne_lif field contains the logical interface to which this event belongs.

The hne_data field contains a pointer to a sockaddr structure with the address in it.

The hne_datalen field contains the size of the sockaddr structure.

Packet Filtering Functions

/*


* Data management functions
*/
net_data_t net_lookup(const char *protocol);
net_data_t net_register(const net_info_t *);
int        net_release(net_data_t);
int        net_unregister(net_data_t);
net_data_t net_walk(net_data_t);


/*


* Accessor functions
*/
net_event_t net_register_event(net_data_t, hook_event_t *);
int         net_unregister_event(net_data_t, net_event_t);
net_hook_t  net_register_hook(net_data_t, char *event, struct hook *);
int         net_unregister_hook(net_data_t, net_hook_t);


int         net_ispartialchecksum(net_data_t *, mblk_t *);
int         net_isvalidchecksum(net_data_t *, mblk_t *);


int         net_getforwarding(net_data_t *, phy_if_t);
int         net_setforwarding(net_data_t *, phy_if_t, int);
int         net_getifname(net_data_t *, phy_if_t, net_if_t,
                          char *buffer, const size_t buflen);
int         net_getmtu(net_data_t *, phy_if_t, net_if_t);
int         net_getpmtuenabled(net_data_t *);
int         net_getlifaddr(net_data_t *, phy_if_t, net_if_t,
                           int nelem, net_ifaddr_t type[],
                           struct sockaddr storage[]);
phy_if_t    net_phygetnext(net_data_t *, phy_if_t);
phy_if_t    net_phylookup(net_data_t *, const char *);
net_if_t    net_lifgetnext(net_data_t *, net_if_t);


int net_inject(net_data_tb*, inject_t, net_inject_t *);
phy_if_t net_routeto(net_data_t *, struct sockaddr *);

Defining a Network Protocol Within the netinfo Interface

There are several steps involved in creating netinfo support for a new layer 3 protocol. This section details the framework that is required to support IPv4 in the netinfo interface.

Declaration of the net_info_t Structure

Register a net_info_t structure upon protocol startup. This gives the netinfo interface a reference to the implementation of the features that are available in that protocol. In the case of IPv4, the structure is declared as:

net_info_t ipv4info = { NETINFO_VERSION,
                  AF_INET,
                  ip_inject,
                  ip_getifname,
                  ip_getmtu,
                  ip_getpmtuenabled,
                  ip_getforwarding,
                  ip_setforwarding,
                  ip_getifaddr,
                  ip_getifaddrset,
                  ip_iflookup,
                  ip_ifgetnext,
                  ip_routeto};

Initialize the first field of the net_info_t structure, neti_version, as NETINFO_VERSION. This could safely be omitted as this initialization is also done in the public header, neti.h. The second field, neti_family, is set to AF_INET to indicate IPv4. This is the same neti_family field that is passed in and matched during a call to the net_lookup() function.

Selecting an Indexing Scheme

Consumers of the netinfo interface use the net_lookup() function to obtain a particular net_if_t reference to the interface to operate on.

The IP implementation of the netinfo interface relies on the internal interface tree in the Solaris IP module. The value of net_if_t corresponds to the index of the interface node in the AVL tree that IP uses internally. By reusing the indexing in this tree, the data lookup process in an IP implementation of the netinfo interface becomes faster and easier.

Registering and Deregistering net_info_t

In the IPv4 implementation of the netinfo interface, the ipv4info structure is declared inside of the IP kernel module. After the system loads the IP module, the system registers the module with the netinfo interface by calling net_register(&ipv4info).

When the IP module is unloading, the system deregisters the ipv4info structure by calling net_deregister(ipv4);

The Access Functions of net_info_t

The remaining fields in the net_info_t structure after neti_version and neti_family correspond to the access functions. This section examines the implementation for the neti_getmtu() function.

The neti_getmtu() function pointer has the prototype:

int (*neti_getforwarding)(phy_if_t);

This example has registered a function called ip_getforwarding() with the specified prototype. The function is shown below:

int
ip_getforwarding(phy_if_t phyif)
{
      int ret;

      if (phyif == 0) {
            ret = ip_g_forward; /* return the global property */
      } else {
            /* get the property from an ill */
            ill_t *ill;

            ill = ill_lookup_on_ifindex((uint_t)phyif, 0, NULL, NULL, NULL, NULL);
            if (ill != NULL) {
                  ret = (ill->ill_flags & ILLF_ROUTER) ? 1 : 0;
                  ill_refrele(ill);

               } else
                     ret = -1;
      }

      return (ret);
}

This implementation is specific to the Solaris IPv4 implementation. It reads the IP module global variable ip_g_forward to determine whether or not forwarding is enabled for a specific node. In this case, ip_g_forward is global for IP, so all interfaces will have the same forwarding state.

Previous Contents Index Next
Company Info Contact Terms of Use Privacy Copyright 1994-2008 Sun Microsystems, Inc.