1/** 2 * @file 3 * lwIP network interface abstraction 4 * 5 */ 6 7/* 8 * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without modification, 12 * are permitted provided that the following conditions are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, 15 * this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright notice, 17 * this list of conditions and the following disclaimer in the documentation 18 * and/or other materials provided with the distribution. 19 * 3. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 25 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 31 * OF SUCH DAMAGE. 32 * 33 * This file is part of the lwIP TCP/IP stack. 34 * 35 * Author: Adam Dunkels <adam@sics.se> 36 * 37 */ 38 39#include "lwip/opt.h" 40 41#include "lwip/def.h" 42#include "lwip/ip_addr.h" 43#include "lwip/netif.h" 44#include "lwip/tcp.h" 45#include "lwip/snmp.h" 46#include "lwip/igmp.h" 47#include "netif/etharp.h" 48#if ENABLE_LOOPBACK 49#include "lwip/sys.h" 50#if LWIP_NETIF_LOOPBACK_MULTITHREADING 51#include "lwip/tcpip.h" 52#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ 53#endif /* ENABLE_LOOPBACK */ 54 55#if LWIP_NETIF_STATUS_CALLBACK 56#define NETIF_STATUS_CALLBACK(n) { if (n->status_callback) (n->status_callback)(n); } 57#else 58#define NETIF_STATUS_CALLBACK(n) { /* NOP */ } 59#endif /* LWIP_NETIF_STATUS_CALLBACK */ 60 61#if LWIP_NETIF_LINK_CALLBACK 62#define NETIF_LINK_CALLBACK(n) { if (n->link_callback) (n->link_callback)(n); } 63#else 64#define NETIF_LINK_CALLBACK(n) { /* NOP */ } 65#endif /* LWIP_NETIF_LINK_CALLBACK */ 66 67struct netif *netif_list; 68struct netif *netif_default; 69 70/** 71 * Add a network interface to the list of lwIP netifs. 72 * 73 * @param netif a pre-allocated netif structure 74 * @param ipaddr IP address for the new netif 75 * @param netmask network mask for the new netif 76 * @param gw default gateway IP address for the new netif 77 * @param state opaque data passed to the new netif 78 * @param init callback function that initializes the interface 79 * @param input callback function that is called to pass 80 * ingress packets up in the protocol layer stack. 81 * 82 * @return netif, or NULL if failed. 83 */ 84struct netif *netif_add(struct netif *netif, struct ip_addr *ipaddr, 85 struct ip_addr *netmask, struct ip_addr *gw, 86 void *state, err_t(*init) (struct netif * netif), 87 err_t(*input) (struct pbuf * p, struct netif * netif)) 88{ 89 static u8_t netifnum = 0; 90 91 /* reset new interface configuration state */ 92 netif->ip_addr.addr = 0; 93 netif->netmask.addr = 0; 94 netif->gw.addr = 0; 95 netif->flags = 0; 96#if LWIP_DHCP 97 /* netif not under DHCP control by default */ 98 netif->dhcp = NULL; 99#endif /* LWIP_DHCP */ 100#if LWIP_AUTOIP 101 /* netif not under AutoIP control by default */ 102 netif->autoip = NULL; 103#endif /* LWIP_AUTOIP */ 104#if LWIP_NETIF_STATUS_CALLBACK 105 netif->status_callback = NULL; 106#endif /* LWIP_NETIF_STATUS_CALLBACK */ 107#if LWIP_NETIF_LINK_CALLBACK 108 netif->link_callback = NULL; 109#endif /* LWIP_NETIF_LINK_CALLBACK */ 110#if LWIP_IGMP 111 netif->igmp_mac_filter = NULL; 112#endif /* LWIP_IGMP */ 113#if ENABLE_LOOPBACK 114 netif->loop_first = NULL; 115 netif->loop_last = NULL; 116#endif /* ENABLE_LOOPBACK */ 117 118 /* remember netif specific state information data */ 119 netif->state = state; 120 netif->num = netifnum++; 121 netif->input = input; 122#if LWIP_NETIF_HWADDRHINT 123 netif->addr_hint = NULL; 124#endif /* LWIP_NETIF_HWADDRHINT */ 125#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS 126 netif->loop_cnt_current = 0; 127#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */ 128 129 netif_set_addr(netif, ipaddr, netmask, gw); 130 131 /* call user specified initialization function for netif */ 132 if (init(netif) != ERR_OK) { 133 return NULL; 134 } 135 136 /* add this netif to the list */ 137 netif->next = netif_list; 138 netif_list = netif; 139 snmp_inc_iflist(); 140 141#if LWIP_IGMP 142 /* start IGMP processing */ 143 if (netif->flags & NETIF_FLAG_IGMP) { 144 igmp_start(netif); 145 } 146#endif /* LWIP_IGMP */ 147 148 LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ", 149 netif->name[0], netif->name[1])); 150 ip_addr_debug_print(NETIF_DEBUG, ipaddr); 151 LWIP_DEBUGF(NETIF_DEBUG, (" netmask ")); 152 ip_addr_debug_print(NETIF_DEBUG, netmask); 153 LWIP_DEBUGF(NETIF_DEBUG, (" gw ")); 154 ip_addr_debug_print(NETIF_DEBUG, gw); 155 LWIP_DEBUGF(NETIF_DEBUG, ("\n")); 156 return netif; 157} 158 159/** 160 * Change IP address configuration for a network interface (including netmask 161 * and default gateway). 162 * 163 * @param netif the network interface to change 164 * @param ipaddr the new IP address 165 * @param netmask the new netmask 166 * @param gw the new default gateway 167 */ 168void 169netif_set_addr(struct netif *netif, struct ip_addr *ipaddr, 170 struct ip_addr *netmask, struct ip_addr *gw) 171{ 172 netif_set_ipaddr(netif, ipaddr); 173 netif_set_netmask(netif, netmask); 174 netif_set_gw(netif, gw); 175} 176 177/** 178 * Remove a network interface from the list of lwIP netifs. 179 * 180 * @param netif the network interface to remove 181 */ 182void netif_remove(struct netif *netif) 183{ 184 if (netif == NULL) 185 return; 186 187#if LWIP_IGMP 188 /* stop IGMP processing */ 189 if (netif->flags & NETIF_FLAG_IGMP) { 190 igmp_stop(netif); 191 } 192#endif /* LWIP_IGMP */ 193 194 snmp_delete_ipaddridx_tree(netif); 195 196 /* is it the first netif? */ 197 if (netif_list == netif) { 198 netif_list = netif->next; 199 snmp_dec_iflist(); 200 } else { 201 /* look for netif further down the list */ 202 struct netif *tmpNetif; 203 204 for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) { 205 if (tmpNetif->next == netif) { 206 tmpNetif->next = netif->next; 207 snmp_dec_iflist(); 208 break; 209 } 210 } 211 if (tmpNetif == NULL) 212 return; /* we didn't find any netif today */ 213 } 214 /* this netif is default? */ 215 if (netif_default == netif) 216 /* reset default netif */ 217 netif_set_default(NULL); 218 LWIP_DEBUGF(NETIF_DEBUG, ("netif_remove: removed netif\n")); 219} 220 221/** 222 * Find a network interface by searching for its name 223 * 224 * @param name the name of the netif (like netif->name) plus concatenated number 225 * in ascii representation (e.g. 'en0') 226 */ 227struct netif *netif_find(char *name) 228{ 229 struct netif *netif; 230 u8_t num; 231 232 if (name == NULL) { 233 return NULL; 234 } 235 236 num = name[2] - '0'; 237 238 for (netif = netif_list; netif != NULL; netif = netif->next) { 239 if (num == netif->num && 240 name[0] == netif->name[0] && name[1] == netif->name[1]) { 241 LWIP_DEBUGF(NETIF_DEBUG, 242 ("netif_find: found %c%c\n", name[0], name[1])); 243 return netif; 244 } 245 } 246 LWIP_DEBUGF(NETIF_DEBUG, 247 ("netif_find: didn't find %c%c\n", name[0], name[1])); 248 return NULL; 249} 250 251/** 252 * Change the IP address of a network interface 253 * 254 * @param netif the network interface to change 255 * @param ipaddr the new IP address 256 * 257 * @note call netif_set_addr() if you also want to change netmask and 258 * default gateway 259 */ 260void netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr) 261{ 262 /* TODO: Handling of obsolete pcbs */ 263 /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */ 264#if LWIP_TCP 265 struct tcp_pcb *pcb; 266 struct tcp_pcb_listen *lpcb; 267 268 /* address is actually being changed? */ 269 if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) { 270 /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ 271 LWIP_DEBUGF(NETIF_DEBUG | 1, 272 ("netif_set_ipaddr: netif address being changed\n")); 273 pcb = tcp_active_pcbs; 274 while (pcb != NULL) { 275 /* PCB bound to current local interface address? */ 276 if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { 277 /* this connection must be aborted */ 278 struct tcp_pcb *next = pcb->next; 279 280 LWIP_DEBUGF(NETIF_DEBUG | 1, 281 ("netif_set_ipaddr: aborting TCP pcb %p\n", 282 (void *) pcb)); 283 tcp_abort(pcb); 284 pcb = next; 285 } else { 286 pcb = pcb->next; 287 } 288 } 289 for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; 290 lpcb = lpcb->next) { 291 /* PCB bound to current local interface address? */ 292 if ((!(ip_addr_isany(&(lpcb->local_ip)))) && 293 (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr)))) { 294 /* The PCB is listening to the old ipaddr and 295 * is set to listen to the new one instead */ 296 ip_addr_set(&(lpcb->local_ip), ipaddr); 297 } 298 } 299 } 300#endif 301 snmp_delete_ipaddridx_tree(netif); 302 snmp_delete_iprteidx_tree(0, netif); 303 /* set new IP address to netif */ 304 ip_addr_set(&(netif->ip_addr), ipaddr); 305 snmp_insert_ipaddridx_tree(netif); 306 snmp_insert_iprteidx_tree(0, netif); 307 308 LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 3, 309 ("netif: IP address of interface %c%c set to %" U16_F ".%" U16_F 310 ".%" U16_F ".%" U16_F "\n", netif->name[0], netif->name[1], 311 ip4_addr1(&netif->ip_addr), ip4_addr2(&netif->ip_addr), 312 ip4_addr3(&netif->ip_addr), ip4_addr4(&netif->ip_addr))); 313} 314 315/** 316 * Change the default gateway for a network interface 317 * 318 * @param netif the network interface to change 319 * @param gw the new default gateway 320 * 321 * @note call netif_set_addr() if you also want to change ip address and netmask 322 */ 323void netif_set_gw(struct netif *netif, struct ip_addr *gw) 324{ 325 ip_addr_set(&(netif->gw), gw); 326 LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 3, 327 ("netif: GW address of interface %c%c set to %" U16_F ".%" U16_F 328 ".%" U16_F ".%" U16_F "\n", netif->name[0], netif->name[1], 329 ip4_addr1(&netif->gw), ip4_addr2(&netif->gw), 330 ip4_addr3(&netif->gw), ip4_addr4(&netif->gw))); 331} 332 333/** 334 * Change the netmask of a network interface 335 * 336 * @param netif the network interface to change 337 * @param netmask the new netmask 338 * 339 * @note call netif_set_addr() if you also want to change ip address and 340 * default gateway 341 */ 342void netif_set_netmask(struct netif *netif, struct ip_addr *netmask) 343{ 344 snmp_delete_iprteidx_tree(0, netif); 345 /* set new netmask to netif */ 346 ip_addr_set(&(netif->netmask), netmask); 347 snmp_insert_iprteidx_tree(0, netif); 348 LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | 3, 349 ("netif: netmask of interface %c%c set to %" U16_F ".%" U16_F 350 ".%" U16_F ".%" U16_F "\n", netif->name[0], netif->name[1], 351 ip4_addr1(&netif->netmask), ip4_addr2(&netif->netmask), 352 ip4_addr3(&netif->netmask), ip4_addr4(&netif->netmask))); 353} 354 355/** 356 * Set a network interface as the default network interface 357 * (used to output all packets for which no specific route is found) 358 * 359 * @param netif the default network interface 360 */ 361void netif_set_default(struct netif *netif) 362{ 363 if (netif == NULL) { 364 /* remove default route */ 365 snmp_delete_iprteidx_tree(1, netif); 366 } else { 367 /* install default route */ 368 snmp_insert_iprteidx_tree(1, netif); 369 } 370 netif_default = netif; 371 LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", 372 netif ? netif->name[0] : '\'', 373 netif ? netif->name[1] : '\'')); 374} 375 376/** 377 * Bring an interface up, available for processing 378 * traffic. 379 * 380 * @note: Enabling DHCP on a down interface will make it come 381 * up once configured. 382 * 383 * @see dhcp_start() 384 */ 385void netif_set_up(struct netif *netif) 386{ 387 if (!(netif->flags & NETIF_FLAG_UP)) { 388 netif->flags |= NETIF_FLAG_UP; 389 390#if LWIP_SNMP 391 snmp_get_sysuptime(&netif->ts); 392#endif /* LWIP_SNMP */ 393 394 NETIF_LINK_CALLBACK(netif); 395 NETIF_STATUS_CALLBACK(netif); 396 397#if LWIP_ARP 398 /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ 399 if (netif->flags & NETIF_FLAG_ETHARP) { 400 etharp_gratuitous(netif); 401 } 402#endif /* LWIP_ARP */ 403 404 } 405} 406 407/** 408 * Bring an interface down, disabling any traffic processing. 409 * 410 * @note: Enabling DHCP on a down interface will make it come 411 * up once configured. 412 * 413 * @see dhcp_start() 414 */ 415void netif_set_down(struct netif *netif) 416{ 417 if (netif->flags & NETIF_FLAG_UP) { 418 netif->flags &= ~NETIF_FLAG_UP; 419#if LWIP_SNMP 420 snmp_get_sysuptime(&netif->ts); 421#endif 422 423 NETIF_LINK_CALLBACK(netif); 424 NETIF_STATUS_CALLBACK(netif); 425 } 426} 427 428/** 429 * Ask if an interface is up 430 */ 431u8_t netif_is_up(struct netif *netif) 432{ 433 return (netif->flags & NETIF_FLAG_UP) ? 1 : 0; 434} 435 436#if LWIP_NETIF_STATUS_CALLBACK 437/** 438 * Set callback to be called when interface is brought up/down 439 */ 440void netif_set_status_callback(struct netif *netif, 441 void (*status_callback) (struct netif * netif)) 442{ 443 if (netif) 444 netif->status_callback = status_callback; 445} 446#endif /* LWIP_NETIF_STATUS_CALLBACK */ 447 448#if LWIP_NETIF_LINK_CALLBACK 449/** 450 * Called by a driver when its link goes up 451 */ 452void netif_set_link_up(struct netif *netif) 453{ 454 netif->flags |= NETIF_FLAG_LINK_UP; 455 456#if LWIP_ARP 457 /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */ 458 if (netif->flags & NETIF_FLAG_ETHARP) { 459 etharp_gratuitous(netif); 460 } 461#endif /* LWIP_ARP */ 462 463#if LWIP_IGMP 464 /* resend IGMP memberships */ 465 if (netif->flags & NETIF_FLAG_IGMP) { 466 igmp_report_groups(netif); 467 } 468#endif /* LWIP_IGMP */ 469 470 NETIF_LINK_CALLBACK(netif); 471} 472 473/** 474 * Called by a driver when its link goes down 475 */ 476void netif_set_link_down(struct netif *netif) 477{ 478 netif->flags &= ~NETIF_FLAG_LINK_UP; 479 NETIF_LINK_CALLBACK(netif); 480} 481 482/** 483 * Ask if a link is up 484 */ 485u8_t netif_is_link_up(struct netif *netif) 486{ 487 return (netif->flags & NETIF_FLAG_LINK_UP) ? 1 : 0; 488} 489 490/** 491 * Set callback to be called when link is brought up/down 492 */ 493void netif_set_link_callback(struct netif *netif, 494 void (*link_callback) (struct netif * netif)) 495{ 496 if (netif) { 497 netif->link_callback = link_callback; 498 } 499} 500#endif /* LWIP_NETIF_LINK_CALLBACK */ 501 502#if ENABLE_LOOPBACK 503/** 504 * Send an IP packet to be received on the same netif (loopif-like). 505 * The pbuf is simply copied and handed back to netif->input. 506 * In multithreaded mode, this is done directly since netif->input must put 507 * the packet on a queue. 508 * In callback mode, the packet is put on an internal queue and is fed to 509 * netif->input by netif_poll(). 510 * 511 * @param netif the lwip network interface structure 512 * @param p the (IP) packet to 'send' 513 * @param ipaddr the ip address to send the packet to (not used) 514 * @return ERR_OK if the packet has been sent 515 * ERR_MEM if the pbuf used to copy the packet couldn't be allocated 516 */ 517err_t 518netif_loop_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr) 519{ 520 struct pbuf *r; 521 err_t err; 522 struct pbuf *last; 523 524#if LWIP_LOOPBACK_MAX_PBUFS 525 u8_t clen = 0; 526#endif /* LWIP_LOOPBACK_MAX_PBUFS */ 527 SYS_ARCH_DECL_PROTECT(lev); 528 LWIP_UNUSED_ARG(ipaddr); 529 530 /* Allocate a new pbuf */ 531 r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); 532 if (r == NULL) { 533 return ERR_MEM; 534 } 535#if LWIP_LOOPBACK_MAX_PBUFS 536 clen = pbuf_clen(r); 537 /* check for overflow or too many pbuf on queue */ 538 if (((netif->loop_cnt_current + clen) < netif->loop_cnt_current) || 539 ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) { 540 pbuf_free(r); 541 r = NULL; 542 return ERR_MEM; 543 } 544 netif->loop_cnt_current += clen; 545#endif /* LWIP_LOOPBACK_MAX_PBUFS */ 546 547 /* Copy the whole pbuf queue p into the single pbuf r */ 548 if ((err = pbuf_copy(r, p)) != ERR_OK) { 549 pbuf_free(r); 550 r = NULL; 551 return err; 552 } 553 554 /* Put the packet on a linked list which gets emptied through calling 555 netif_poll(). */ 556 557 /* let last point to the last pbuf in chain r */ 558 for (last = r; last->next != NULL; last = last->next); 559 560 SYS_ARCH_PROTECT(lev); 561 if (netif->loop_first != NULL) { 562 LWIP_ASSERT("if first != NULL, last must also be != NULL", 563 netif->loop_last != NULL); 564 netif->loop_last->next = r; 565 netif->loop_last = last; 566 } else { 567 netif->loop_first = r; 568 netif->loop_last = last; 569 } 570 SYS_ARCH_UNPROTECT(lev); 571 572#if LWIP_NETIF_LOOPBACK_MULTITHREADING 573 /* For multithreading environment, schedule a call to netif_poll */ 574 tcpip_callback((void (*)(void *)) (netif_poll), netif); 575#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ 576 577 return ERR_OK; 578} 579 580/** 581 * Call netif_poll() in the main loop of your application. This is to prevent 582 * reentering non-reentrant functions like tcp_input(). Packets passed to 583 * netif_loop_output() are put on a list that is passed to netif->input() by 584 * netif_poll(). 585 */ 586void netif_poll(struct netif *netif) 587{ 588 struct pbuf *in; 589 590 SYS_ARCH_DECL_PROTECT(lev); 591 592 do { 593 /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */ 594 SYS_ARCH_PROTECT(lev); 595 in = netif->loop_first; 596 if (in != NULL) { 597 struct pbuf *in_end = in; 598 599#if LWIP_LOOPBACK_MAX_PBUFS 600 u8_t clen = pbuf_clen(in); 601 602 /* adjust the number of pbufs on queue */ 603 LWIP_ASSERT("netif->loop_cnt_current underflow", 604 ((netif->loop_cnt_current - clen) < 605 netif->loop_cnt_current)); 606 netif->loop_cnt_current -= clen; 607#endif /* LWIP_LOOPBACK_MAX_PBUFS */ 608 while (in_end->len != in_end->tot_len) { 609 LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", 610 in_end->next != NULL); 611 in_end = in_end->next; 612 } 613 /* 'in_end' now points to the last pbuf from 'in' */ 614 if (in_end == netif->loop_last) { 615 /* this was the last pbuf in the list */ 616 netif->loop_first = netif->loop_last = NULL; 617 } else { 618 /* pop the pbuf off the list */ 619 netif->loop_first = in_end->next; 620 LWIP_ASSERT("should not be null since first != last!", 621 netif->loop_first != NULL); 622 } 623 /* De-queue the pbuf from its successors on the 'loop_' list. */ 624 in_end->next = NULL; 625 } 626 SYS_ARCH_UNPROTECT(lev); 627 628 if (in != NULL) { 629 /* loopback packets are always IP packets! */ 630 if (ip_input(in, netif) != ERR_OK) { 631 pbuf_free(in); 632 } 633 /* Don't reference the packet any more! */ 634 in = NULL; 635 } 636 /* go on while there is a packet on the list */ 637 } while (netif->loop_first != NULL); 638} 639 640#if !LWIP_NETIF_LOOPBACK_MULTITHREADING 641/** 642 * Calls netif_poll() for every netif on the netif_list. 643 */ 644void netif_poll_all(void) 645{ 646 struct netif *netif = netif_list; 647 648 /* loop through netifs */ 649 while (netif != NULL) { 650 netif_poll(netif); 651 /* proceed to next network interface */ 652 netif = netif->next; 653 } 654} 655#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ 656#endif /* ENABLE_LOOPBACK */ 657