Description of the "concap" encapsulation protocol interface
The "concap" interface is intended to be used by network device
drivers that need to process an encapsulation protocol.
It is assumed that the protocol interacts with a linux network device by
- data transmission
- connection control (establish, release)
Thus, the mnemonic: "CONnection CONtrolling eNCAPsulation Protocol".
This is currently only used inside the isdn subsystem. But it might
also be useful to other kinds of network devices. Thus, if you want
to suggest changes that improve usability or performance of the
interface, please let me know. I'm willing to include them in future
releases (even if I needed to adapt the current isdn code to the
Why is this useful?
The encapsulation protocol used on top of WAN connections or permanent
point-to-point links are frequently chosen upon bilateral agreement.
Thus, a device driver for a certain type of hardware must support
several different encapsulation protocols at once.
The isdn device driver did already support several different
encapsulation protocols. The encapsulation protocol is configured by a
user space utility (isdnctrl). The isdn network interface code then
uses several case statements which select appropriate actions
depending on the currently configured encapsulation protocol.
In contrast, LAN network interfaces always used a single encapsulation
protocol which is unique to the hardware type of the interface. The LAN
encapsulation is usually done by just sticking a header on the data. Thus,
traditional linux network device drivers used to process the
encapsulation protocol directly (usually by just providing a hard_header()
method in the device structure) using some hardware type specific support
functions. This is simple, direct and efficient. But it doesn't fit all
the requirements for complex WAN encapsulations.
The configurability of the encapsulation protocol to be used
makes isdn network interfaces more flexible, but also much more
complex than traditional lan network interfaces.
Many Encapsulation protocols used on top of WAN connections will not just
stick a header on the data. They also might need to set up or release
the WAN connection. They also might want to send other data for their
private purpose over the wire, e.g. ppp does a lot of link level
negotiation before the first piece of user data can be transmitted.
Such encapsulation protocols for WAN devices are typically more complex
than encapsulation protocols for lan devices. Thus, network interface
code for typical WAN devices also tends to be more complex.
In order to support Linux' x25 PLP implementation on top of
isdn network interfaces I could have introduced yet another branch to
the various case statements inside drivers/isdn/isdn_net.c.
This eventually made isdn_net.c even more complex. In addition, it made
isdn_net.c harder to maintain. Thus, by identifying an abstract
interface between the network interface code and the encapsulation
protocol, complexity could be reduced and maintainability could be
Likewise, a similar encapsulation protocol will frequently be needed by
several different interfaces of even different hardware type, e.g. the
synchronous ppp implementation used by the isdn driver and the
asynchronous ppp implementation used by the ppp driver have a lot of
similar code in them. By cleanly separating the encapsulation protocol
from the hardware specific interface stuff such code could be shared
better in future.
When operating over dial-up-connections (e.g. telephone lines via modem,
non-permanent virtual circuits of wide area networks, ISDN) many
encapsulation protocols will need to control the connection. Therefore,
some basic connection control primitives are supported. The type and
semantics of the connection (i.e the ISO layer where connection service
is provided) is outside our scope and might be different depending on
the encapsulation protocol used, e.g. for a ppp module using our service
on top of a modem connection a connect_request will result in dialing
a (somewhere else configured) remote phone number. For an X25-interface
module (LAPB semantics, as defined in Documentation/networking/x25-iface.txt)
a connect_request will ask for establishing a reliable lapb
The encapsulation protocol currently provides the following
service primitives to the network device.
- create a new encapsulation protocol instance
- delete encapsulation protocol instance and free all its resources
- initialize (open) the encapsulation protocol instance for use.
- deactivate (close) an encapsulation protocol instance.
- process (xmit) data handed down by upper protocol layer
- receive data from lower (hardware) layer
- process connect indication from lower (hardware) layer
- process disconnect indication from lower (hardware) layer
The network interface driver accesses those primitives via callbacks
provided by the encapsulation protocol instance within a
/* create a new encapsulation protocol instance of same type */
struct concap_proto * (*proto_new) (void);
/* delete encapsulation protocol instance and free all its resources.
cprot may no longer be referenced after calling this */
void (*proto_del)(struct concap_proto *cprot);
/* initialize the protocol's data. To be called at interface startup
or when the device driver resets the interface. All services of the
encapsulation protocol may be used after this*/
int (*restart)(struct concap_proto *cprot,
struct net_device *ndev,
struct concap_device_ops *dops);
/* deactivate an encapsulation protocol instance. The encapsulation
protocol may not call any *dops methods after this. */
int (*close)(struct concap_proto *cprot);
/* process a frame handed down to us by upper layer */
int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb);
/* to be called for each data entity received from lower layer*/
int (*data_ind)(struct concap_proto *cprot, struct sk_buff *skb);
/* to be called when a connection was set up/down.
Protocols that don't process these primitives might fill in
dummy methods here */
int (*connect_ind)(struct concap_proto *cprot);
int (*disconn_ind)(struct concap_proto *cprot);
The data structures are defined in the header file include/linux/concap.h.
A Network interface using encapsulation protocols must also provide
some service primitives to the encapsulation protocol:
- request data being submitted by lower layer (device hardware)
- request a connection being set up by lower layer
- request a connection being released by lower layer
The encapsulation protocol accesses those primitives via callbacks
provided by the network interface within a struct concap_device_ops.
/* to request data be submitted by device */
int (*data_req)(struct concap_proto *, struct sk_buff *);
/* Control methods must be set to NULL by devices which do not
support connection control. */
/* to request a connection be set up */
int (*connect_req)(struct concap_proto *);
/* to request a connection be released */
int (*disconn_req)(struct concap_proto *);
The network interface does not explicitly provide a receive service
because the encapsulation protocol directly calls netif_rx().
An encapsulation protocol itself is actually the
struct net_device *net_dev; /* net device using our service */
struct concap_device_ops *dops; /* callbacks provided by device */
struct concap_proto_ops *pops; /* callbacks provided by us */
void *proto_data; /* protocol specific private data, to
be accessed via *pops methods only*/
Most of this is filled in when the device requests the protocol to
be reset (opend). The network interface must provide the net_dev and
dops pointers. Other concap_proto members should be considered private
data that are only accessed by the pops callback functions. Likewise,
a concap proto should access the network device's private data
only by means of the callbacks referred to by the dops pointer.
A possible extended device structure which uses the connection controlling
encapsulation services could look like this:
struct net_device net_dev;
struct my_priv /* device->local stuff */
/* the my_priv struct might contain a
struct concap_device_ops *dops;
to provide the device specific callbacks
struct concap_proto *cprot; /* callbacks provided by protocol */
The concept of the concap proto might help to reuse protocol code and
reduce the complexity of certain network interface implementations.
The trade off is that it introduces yet another procedure call layer
when processing the protocol. This has of course some impact on
performance. However, typically the concap interface will be used by
devices attached to slow lines (like telephone, isdn, leased synchronous
lines). For such slow lines, the overhead is probably negligible.
This might no longer hold for certain high speed WAN links (like
If general linux network interfaces explicitly supported concap
protocols (e.g. by a member struct concap_proto* in struct net_device)
then the interface of the service function could be changed
by passing a pointer of type (struct net_device*) instead of
type (struct concap_proto*). Doing so would make many of the service
functions compatible to network device support functions.
e.g. instead of the concap protocol's service function
int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb);
we could have
int (*encap_and_xmit)(struct net_device *ndev, struct sk_buff *skb);
As this is compatible to the dev->hard_start_xmit() method, the device
driver could directly register the concap protocol's encap_and_xmit()
function as its hard_start_xmit() method. This would eliminate one
procedure call layer.
The device's data request function could also be defined as
int (*data_req)(struct net_device *ndev, struct sk_buff *skb);
This might even allow for some protocol stacking. And the network
interface might even register the same data_req() function directly
as its hard_start_xmit() method when a zero layer encapsulation
protocol is configured. Thus, eliminating the performance penalty
of the concap interface when a trivial concap protocol is used.
Nevertheless, the device remains able to support encapsulation