Required settings[]
Even if you are not creating an Ethernet driver, the netif/ethernetif.c file offers a good outline for what a driver should include. The following is a list of things that must be defined.
Functions[]
Initialization
err_t myif_init(struct netif *netif)
- This is the initialization function that should be passed tonetif_add
(see Network interfaces management in the Application Developers Manual for an example), where it will be run.
Output
err_t myif_link_output(struct netif *netif, struct pbuf *p)
- Called when a raw link packet is ready to be transmitted. This function should not add any more headers. You must setnetif->linkoutput
to the address of this function.err_t myif_output(struct netif *netif, struct pbuf *p
, ip_addr_t *ipaddr)
- Called byip_output
when a packet is ready for transmission. Any link headers will be added here. This function should call themyif_link_output
function when the packet is ready. You must setnetif->output
to the address of this function. If your driver supports ARP, you can simply setnetif->output
toetharp_output
.
Input
- When you have received a packet (do not remove link headers!), you should pass the packet to
my_netif->input
, which should have been set bynetif_add
to the appropriate function (see Network interfaces management). - Do not pass received packets to one of the input functions directly! Whether this be
ethernet_input
orip_input
: things might work like this, but you lose portability and might be incompatible to future changes and risk threading problems! (Plus you risk annoying people on the mailing list with the problems you get by doing this - there will be problems, trust me :-) - This function must be set to the correct input function by your initialization code (via
netif_add
):- With NO_SYS=0, this must be set to
tcpip_input
for all netif types (whether ethernet, PPP, slipif, etc.) - With NO_SYS=1,
- For ethernet netifs, this must be set to
ethernet_input
(pass the pbuf including link headers) - For non-ethernet netifs, this must be set to
ip_input
(pass the pbuf without link headers,p->payload
pointing to the IP header)
- For ethernet netifs, this must be set to
- With NO_SYS=0, this must be set to
struct netif
settings[]
The following structure variables should be set during initialization:
State
my_netif->state
- Point this to any internal structure that you would like to keep track of for the "state" of your interface. This could include status information, statistics, or anything else.my_netif->hwaddr_len
- The number of bytes in the link address (e.g., MAC address for Ethernet)my_netif->hwaddr[]
- The hardware address itself.my_netif->mtu
- The MTU (maximum transmission unit) for the interface. This defines the maximum sized packet that may be sent on the link, including headers. If an application attempts to send a packet larger than this, lwIP will split the packet up into pieces of this size.my_netif->name[2]
- Two-character name, like "en" for Ethernet. This can be used to get a netif by name, vianetif_find
my_netif->num
- An optional number to indicate the number in this "series", if there are multiple netif's of the same type. For example, perhaps there are two interfaces named "en"; they can be differentiated by this number. This is completely optional and largely unused by lwIP.
Functions
my_netif->output
- Set this equal to themyif_output
described above.my_netif->link_output
- Set this equal to themyif_link_output
described above.my_netif->input
- Do not set this. This is set bynetif_add
.
Flags
my_netif->flags
- Should set any of the following flags that apply to this interface.NETIF_FLAG_BROADCAST
- If this interface can transmit broadcast packets.NETIF_FLAG_POINTTOPOINT
- If this interface is on an end of a PPP connection.NETIF_FLAG_ETHARP
- If this interface uses ARP (see the section below for more information).NETIF_FLAG_LINK_UP
- Set this flag when the link is established (see note below for more capability).
Do not set these flags directly, since other flags are set and reset elsewhere in lwIP (like NETIF_FLAG_UP
). Set via an OR, for example my_netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
Optional variables The following variables are disabled by default; if you would like to use these, then make sure to set the right option inside of lwipopts.h.
my_netif->hostname
(optionLWIP_NETIF_HOSTNAME
) - The hostname assigned to this interface.- Various SNMP settings (option
LWIP_SNMP
)
Link change events[]
A new feature in version 1.3.0 is the ability to initiate link-change events by the driver. If you set the LWIP_NETIF_LINK_CALLBACK
in lwipopts.h, then your driver should call the netif_set_link_up
and netif_set_link_down
functions instead of setting the NETIF_FLAG_LINK_UP
flag directly. (If you are using the tcpip thread, then you should call tcpip_callback
with an argument of one of these two functions; this will protect against concurrent access, specifically in the ARP modules.)
A link change will trigger certain events in the code. Currently, when link goes up, the gratuitous ARP is generated.
Incorporating ARP for your driver[]
If the interface supports ARP, then you should set the NETIF_FLAG_ETHARP
flag in my_netif->flags
. There are a couple options for how to pass packets into the stack, depending on whether you use the optional tcpip thread or not.
Unthreaded[]
Output. When an IP packet is ready for transmission, it calls netif->output
. Most likely, you can set this to directly call the following ARP output function for an interface, which subsequently calls netif->linkoutput
for its frames.
err_t etharp_output()
(Note that function argument order has changed in version 1.3.0; prior to this version, it was necessary to have a wrapper function to swap the arguments.)
Input. Any incoming packet (whether it is an ARP packet or an IP packet) should be passed to the netif->input
function:
my_netif->input()
This is the function which has been set by your initialization code vianetif_add
. Depending on the type of the netif:- Ethernet netif:
- This should have been set to
ethernet_input()
. - The full packet (including ARP header) must be passed.
- This should have been set to
- Non-ethernet netif (e.g. PPP or slipif):
- This should have been set to
ip_input()
. - Link headers must be removed (pbuf->payload must point to the IP header)
- This should have been set to
- Ethernet netif:
Timer. Finally, the following timer must be invoked every A
RP_TMR_INTERVAL
milliseconds (typically 5000, or 5 seconds):
void etharp_tmr()
With the tcpip thread[]
Output. For output, we use the same function as above, since we assume only the tcpip thread will call this function.
err_t etharp_output()
(Note that function argument order has changed in version 1.3.0; prior to this version, it was necessary to have a wrapper function to swap the arguments.)
Input. For input, we must pass the packet into the tcpip thread for processing in order to protect the changes to the ARP table. You need to define ETHARP_TCPIP_ETHINPUT
and use the following function:
my_netif->input()
(which should have been set totcpip_input()
by your application vianetif_add
) - for all incoming packets
Timer. The timer is already handled by the tcpip thread.
Gratuitous ARP[]
A "gratuitous ARP" can be generated by a call etharp_query(our_netif, its_ip_addr, NULL)
(see RFC 3220, Section 4.6). Starting in version 1.3.0, the gratuitous ARP is generated by netif_set_up()
and should not be done in the driver or application code.
Notes on Zero-Copy Network interface drivers[]
- myif_link_output: after LwIP has called this function it frees the pbuf (as soon as it pleases). This would mean that the network driver may not access the buffer after link_output had returned. So link_output would either need to complete the transmit before returning, or it would need to copy the data somewhere else. However it is possible to call
pbuf_ref(p);
inside link_output and callpbuf_free(p);
later, as soon as the buffer is not needed any more (=the package transmit has completed). Yet the network driver may not modify the pbuf, as it may still be used by LwIP, for example for TCP retransmits. The network driver may also not assume that the pbuf memory is actually freed when it callspbuf_free
. In case LwIP still uses the buffer, only the reference counter is decremented.
Implementing a network driver as described above assumes that LwIP may free the pbuf, but it does not modify it after link_output has been called. By looking at the LwIP source code we see that is mostly true for tcp/ip (analysis incomplete by now):- netif/etharp.c has calls to netif->linkoutput() in the following functions:
- etharp_arp_input calls pbuf_free at end of function => ok
- etharp_raw calls pbuf_free at end of function => ok
- etharp_send_ip
- called by update_arp_entry, which calls pbuf_free => ok
- etharp_output: usually called via netif->output()
- etharp_query is called by etharp_output, see above.
- netif/ethernetif.c ?
- netif/ppp/ppp_oe.c ?
- the following files have calls to netif->output()
- core/ipv4/ip.c ?
- core/ipv4/igmp.c ?
- core/ipv4/ip_frag.c ?
- core/ipv6/ip6.c ?
- netif/loopif.c ?
- netif/slipif.c ?
- netif/ethernetif.c ?
- netif/ppp/ppp.c ?
- ...
- tcp: calls to netif->output() via ip_output();/ip_output_if();
- tcp_out.c:
- tcp_output calls pbuf_free after calling ip_output/ipoutput_if => ok
- tcp_rst calls pbuf_free after calling ip_output/ipoutput_if => ok
- tcp_keepalive calls pbuf_free after calling ip_output/ipoutput_if => ok
- tcp_zero_window_probe calls pbuf_free after calling ip_output/ipoutput_if => ok
- tcp_output_segment does not call pbuf_free itself, it is called by tcp_output.
The segments from tcp_output_segment are moved to pcb->unacked. In tcp_in.c they are freed in tcp_receive, l.855. after they have been acknowledged. In case they are resent, they are moved to pcb->unsent in tcp_rexmit_rto(), then sent via tcp_output() and tcp_output_segment(). In tcp_output_segment the tcp header is (re-)calculated and thus the buffer is modified.
- netif/etharp.c has calls to netif->linkoutput() in the following functions:
==> So it currently seems like the tx buffer is only modified during tcp retransmit. That means that the network driver needs to complete its transmit before a retransmit can be initiated by LwIP, which should be feasible in most cases. However, take a look at that mail from the mailing list archive.