/** * Go through timeout list (for this task only) and remove the first matching * entry (subsequent entries remain untouched), even though the timeout has not * triggered yet. * * @param handler callback function that would be called by the timeout * @param arg callback argument that would be passed to handler */ void sys_untimeout(sys_timeout_handler handler, void *arg) { struct sys_timeo *prev_t, *t; LWIP_ASSERT_CORE_LOCKED(); if (next_timeout == NULL) { return; } for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { if ((t->h == handler) && (t->arg == arg)) { /* We have a match */ /* Unlink from previous in list */ if (prev_t == NULL) { next_timeout = t->next; } else { prev_t->next = t->next; } memp_free(MEMP_SYS_TIMEOUT, t); return; } } return; }
/** * @ingroup igmp * Join a group on one network interface. * * @param ifaddr ip address of the network interface which should join a new group * @param groupaddr the ip address of the group which to join * @return ERR_OK if group was joined on the netif(s), an err_t otherwise */ err_t igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr) { err_t err = ERR_VAL; /* no matching interface */ struct netif *netif; LWIP_ASSERT_CORE_LOCKED(); /* make sure it is multicast address */ LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
/** * Called from TCP_REG when registering a new PCB: * the reason is to have the TCP timer only running when * there are active (or time-wait) PCBs. */ void tcp_timer_needed(void) { LWIP_ASSERT_CORE_LOCKED(); /* timer is off but needed again? */ if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { /* enable and start timer */ tcpip_tcp_timer_active = 1; sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL); } }
/** @ingroup smtp * Send an email via the currently selected server, username and password. * * @param from source email address (must be NULL-terminated) * @param to target email address (must be NULL-terminated) * @param subject email subject (must be NULL-terminated) * @param body email body (must be NULL-terminated) * @param callback_fn callback function * @param callback_arg user argument to callback_fn * @returns - ERR_OK if structures were allocated and no error occured starting the connection * (this does not mean the email has been successfully sent!) * - another err_t on error. */ err_t smtp_send_mail(const char* from, const char* to, const char* subject, const char* body, smtp_result_fn callback_fn, void* callback_arg) { struct smtp_session* s; size_t from_len = strlen(from); size_t to_len = strlen(to); size_t subject_len = strlen(subject); size_t body_len = strlen(body); size_t mem_len = sizeof(struct smtp_session); char *sfrom, *sto, *ssubject, *sbody; LWIP_ASSERT_CORE_LOCKED(); mem_len += from_len + to_len + subject_len + body_len + 4; if (mem_len > 0xffff) { /* too long! */ return ERR_MEM; } /* Allocate memory to keep this email's session state */ s = (struct smtp_session *)SMTP_STATE_MALLOC((mem_size_t)mem_len); if (s == NULL) { return ERR_MEM; } /* initialize the structure */ memset(s, 0, mem_len); s->from = sfrom = (char*)s + sizeof(struct smtp_session); s->from_len = (u16_t)from_len; s->to = sto = sfrom + from_len + 1; s->to_len = (u16_t)to_len; s->subject = ssubject = sto + to_len + 1; s->subject_len = (u16_t)subject_len; s->body = sbody = ssubject + subject_len + 1; s->body_len = (u16_t)body_len; /* copy source and target email address */ /* cast to size_t is a hack to cast away constness */ MEMCPY(sfrom, from, from_len + 1); MEMCPY(sto, to, to_len + 1); MEMCPY(ssubject, subject, subject_len + 1); MEMCPY(sbody, body, body_len + 1); s->callback_fn = callback_fn; s->callback_arg = callback_arg; /* call the actual implementation of this function */ return smtp_send_mail_alloced(s); }
/** * @ingroup lwip_nosys * Handle timeouts for NO_SYS==1 (i.e. without using * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout * handler functions when timeouts expire. * * Must be called periodically from your main loop. */ void sys_check_timeouts(void) { u32_t now; LWIP_ASSERT_CORE_LOCKED(); /* Process only timers expired at the start of the function. */ now = sys_now(); do { struct sys_timeo *tmptimeout; sys_timeout_handler handler; void *arg; PBUF_CHECK_FREE_OOSEQ(); tmptimeout = next_timeout; if (tmptimeout == NULL) { return; } if (TIME_LESS_THAN(now, tmptimeout->time)) { return; } /* Timeout has expired */ next_timeout = tmptimeout->next; handler = tmptimeout->h; arg = tmptimeout->arg; current_timeout_due_time = tmptimeout->time; #if LWIP_DEBUG_TIMERNAMES if (handler != NULL) { LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s t=%"U32_F" arg=%p\n", tmptimeout->handler_name, sys_now() - tmptimeout->time, arg)); } #endif /* LWIP_DEBUG_TIMERNAMES */ memp_free(MEMP_SYS_TIMEOUT, tmptimeout); if (handler != NULL) { handler(arg); } LWIP_TCPIP_THREAD_ALIVE(); /* Repeat until all expired timers have been called */ } while (1); }
/** * Create a one-shot timer (aka timeout). Timeouts are processed in the * following cases: * - while waiting for a message using sys_timeouts_mbox_fetch() * - by calling sys_check_timeouts() (NO_SYS==1 only) * * @param msecs time in milliseconds after that the timer should expire * @param handler callback function to call when msecs have elapsed * @param arg argument to pass to the callback function */ #if LWIP_DEBUG_TIMERNAMES void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char *handler_name) #else /* LWIP_DEBUG_TIMERNAMES */ void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg) #endif /* LWIP_DEBUG_TIMERNAMES */ { u32_t next_timeout_time; LWIP_ASSERT_CORE_LOCKED(); LWIP_ASSERT("Timeout time too long, max is LWIP_UINT32_MAX/4 msecs", msecs <= (LWIP_UINT32_MAX / 4)); next_timeout_time = (u32_t)(sys_now() + msecs); /* overflow handled by TIME_LESS_THAN macro */ #if LWIP_DEBUG_TIMERNAMES sys_timeout_abs(next_timeout_time, handler, arg, handler_name); #else sys_timeout_abs(next_timeout_time, handler, arg); #endif }
/** Return the time left before the next timeout is due. If no timeouts are * enqueued, returns 0xffffffff */ u32_t sys_timeouts_sleeptime(void) { u32_t now; LWIP_ASSERT_CORE_LOCKED(); if (next_timeout == NULL) { return SYS_TIMEOUTS_SLEEPTIME_INFINITE; } now = sys_now(); if (TIME_LESS_THAN(next_timeout->time, now)) { return 0; } else { u32_t ret = (u32_t)(next_timeout->time - now); LWIP_ASSERT("invalid sleeptime", ret <= LWIP_MAX_TIMEOUT); return ret; } }
/** @ingroup smtp * Set authentication parameters for next SMTP connection * * @param username login name as passed to the server * @param pass password passed to the server together with username */ err_t smtp_set_auth(const char* username, const char* pass) { size_t uname_len = 0; size_t pass_len = 0; LWIP_ASSERT_CORE_LOCKED(); memset(smtp_auth_plain, 0xfa, 64); if (username != NULL) { uname_len = strlen(username); if (uname_len > SMTP_MAX_USERNAME_LEN) { LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Username is too long, %d instead of %d\n", (int)uname_len, SMTP_MAX_USERNAME_LEN)); return ERR_ARG; } } if (pass != NULL) { #if SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN pass_len = strlen(pass); if (pass_len > SMTP_MAX_PASS_LEN) { LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Password is too long, %d instead of %d\n", (int)uname_len, SMTP_MAX_USERNAME_LEN)); return ERR_ARG; } #else /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("Password not supported as no authentication methods are activated\n")); #endif /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */ } *smtp_auth_plain = 0; if (username != NULL) { smtp_username = smtp_auth_plain + 1; strcpy(smtp_username, username); } if (pass != NULL) { smtp_pass = smtp_auth_plain + uname_len + 2; strcpy(smtp_pass, pass); } smtp_auth_plain_len = uname_len + pass_len + 2; return ERR_OK; }
/** Same as smtp_send_mail_static, but uses a callback function to send body data */ err_t smtp_send_mail_bodycback(const char *from, const char* to, const char* subject, smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg) { struct smtp_session* s; size_t len; LWIP_ASSERT_CORE_LOCKED(); s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session)); if (s == NULL) { return ERR_MEM; } memset(s, 0, sizeof(struct smtp_session)); s->bodydh = (struct smtp_bodydh_state*)SMTP_BODYDH_MALLOC(sizeof(struct smtp_bodydh_state)); if (s->bodydh == NULL) { SMTP_STATE_FREE(s); return ERR_MEM; } memset(s->bodydh, 0, sizeof(struct smtp_bodydh_state)); /* initialize the structure */ s->from = from; len = strlen(from); LWIP_ASSERT("string is too long", len <= 0xffff); s->from_len = (u16_t)len; s->to = to; len = strlen(to); LWIP_ASSERT("string is too long", len <= 0xffff); s->to_len = (u16_t)len; s->subject = subject; len = strlen(subject); LWIP_ASSERT("string is too long", len <= 0xffff); s->subject_len = (u16_t)len; s->body = NULL; LWIP_ASSERT("string is too long", len <= 0xffff); s->callback_fn = callback_fn; s->callback_arg = callback_arg; s->bodydh->callback_fn = bodycback_fn; s->bodydh->state = BDH_SENDING; /* call the actual implementation of this function */ return smtp_send_mail_alloced(s); }
/** @ingroup smtp * Same as smtp_send_mail but takes a struct smtp_send_request as single * parameter which contains all the other parameters. * To be used with tcpip_callback to send mail from interrupt context or from * another thread. * * WARNING: server and authentication must stay untouched until this function has run! * * Usage example: * - allocate a struct smtp_send_request (in a way that is allowed in interrupt context) * - fill the members of the struct as if calling smtp_send_mail * - specify a callback_function * - set callback_arg to the structure itself * - call this function * - wait for the callback function to be called * - in the callback function, deallocate the structure (passed as arg) */ void smtp_send_mail_int(void *arg) { struct smtp_send_request *req = (struct smtp_send_request*)arg; err_t err; LWIP_ASSERT_CORE_LOCKED(); LWIP_ASSERT("smtp_send_mail_int: no argument given", arg != NULL); if (req->static_data) { err = smtp_send_mail_static(req->from, req->to, req->subject, req->body, req->callback_fn, req->callback_arg); } else { err = smtp_send_mail(req->from, req->to, req->subject, req->body, req->callback_fn, req->callback_arg); } if ((err != ERR_OK) && (req->callback_fn != NULL)) { req->callback_fn(req->callback_arg, SMTP_RESULT_ERR_UNKNOWN, 0, err); } }
/** @ingroup smtp * Set IP address or DNS name for next SMTP connection * * @param server IP address (in ASCII representation) or DNS name of the server */ err_t smtp_set_server_addr(const char* server) { size_t len = 0; LWIP_ASSERT_CORE_LOCKED(); if (server != NULL) { /* strlen: returns length WITHOUT terminating 0 byte */ len = strlen(server); } if (len > SMTP_MAX_SERVERNAME_LEN) { return ERR_MEM; } if (len != 0) { MEMCPY(smtp_server, server, len); } smtp_server[len] = 0; /* always OK because of smtp_server[SMTP_MAX_SERVERNAME_LEN + 1] */ return ERR_OK; }
/** @ingroup smtp * Set TLS configuration for next SMTP connection * * @param tls_config TLS configuration */ void smtp_set_tls_config(struct altcp_tls_config *tls_config) { LWIP_ASSERT_CORE_LOCKED(); smtp_server_tls_config = tls_config; }
/** @ingroup smtp * Set TCP port for next SMTP connection * * @param port TCP port */ void smtp_set_server_port(u16_t port) { LWIP_ASSERT_CORE_LOCKED(); smtp_server_port = port; }
/** * @ingroup lwip_nosys * Process received ethernet frames. Using this function instead of directly * calling ip_input and passing ARP frames through etharp in ethernetif_input, * the ARP cache is protected from concurrent access.\n * Don't call directly, pass to netif_add() and call netif->input(). * * @param p the received packet, p->payload pointing to the ethernet header * @param netif the network interface on which the packet was received * * @see LWIP_HOOK_UNKNOWN_ETH_PROTOCOL * @see ETHARP_SUPPORT_VLAN * @see LWIP_HOOK_VLAN_CHECK */ err_t ethernet_input(struct pbuf *p, struct netif *netif) { struct eth_hdr *ethhdr; u16_t type; #if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6 u16_t next_hdr_offset = SIZEOF_ETH_HDR; #endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ LWIP_ASSERT_CORE_LOCKED(); if (p->len <= SIZEOF_ETH_HDR) { /* a packet with only an ethernet header (or less) is not valid for us */ ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); MIB2_STATS_NETIF_INC(netif, ifinerrors); goto free_and_return; } if (p->if_idx == NETIF_NO_INDEX) { p->if_idx = netif_get_index(netif); } /* points to packet payload, which starts with an Ethernet header */ ethhdr = (struct eth_hdr *)p->payload; LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n", (unsigned char)ethhdr->dest.addr[0], (unsigned char)ethhdr->dest.addr[1], (unsigned char)ethhdr->dest.addr[2], (unsigned char)ethhdr->dest.addr[3], (unsigned char)ethhdr->dest.addr[4], (unsigned char)ethhdr->dest.addr[5], (unsigned char)ethhdr->src.addr[0], (unsigned char)ethhdr->src.addr[1], (unsigned char)ethhdr->src.addr[2], (unsigned char)ethhdr->src.addr[3], (unsigned char)ethhdr->src.addr[4], (unsigned char)ethhdr->src.addr[5], lwip_htons(ethhdr->type))); type = ethhdr->type; #if ETHARP_SUPPORT_VLAN if (type == PP_HTONS(ETHTYPE_VLAN)) { struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr *)(((char *)ethhdr) + SIZEOF_ETH_HDR); if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { /* a packet with only an ethernet/vlan header (or less) is not valid for us */ ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); MIB2_STATS_NETIF_INC(netif, ifinerrors); goto free_and_return; } #if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ #ifdef LWIP_HOOK_VLAN_CHECK if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) { #elif defined(ETHARP_VLAN_CHECK_FN) if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { #elif defined(ETHARP_VLAN_CHECK) if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { #endif /* silently ignore this packet: not for our VLAN */ pbuf_free(p); return ERR_OK; } #endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ type = vlan->tpid; next_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; } #endif /* ETHARP_SUPPORT_VLAN */ #if LWIP_ARP_FILTER_NETIF netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, lwip_htons(type)); #endif /* LWIP_ARP_FILTER_NETIF*/ if (ethhdr->dest.addr[0] & 1) { /* this might be a multicast or broadcast packet */ if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) { #if LWIP_IPV4 if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) && (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) { /* mark the pbuf as link-layer multicast */ p->flags |= PBUF_FLAG_LLMCAST; } #endif /* LWIP_IPV4 */ } #if LWIP_IPV6 else if ((ethhdr->dest.addr[0] == LL_IP6_MULTICAST_ADDR_0) && (ethhdr->dest.addr[1] == LL_IP6_MULTICAST_ADDR_1)) { /* mark the pbuf as link-layer multicast */ p->flags |= PBUF_FLAG_LLMCAST; } #endif /* LWIP_IPV6 */ else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { /* mark the pbuf as link-layer broadcast */ p->flags |= PBUF_FLAG_LLBCAST; } } switch (type) { #if LWIP_IPV4 && LWIP_ARP /* IP packet? */ case PP_HTONS(ETHTYPE_IP): if (!(netif->flags & NETIF_FLAG_ETHARP)) { goto free_and_return; } /* skip Ethernet header */ if ((p->len < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ethernet_input: IPv4 packet dropped, too short (%"U16_F"/%"U16_F")\n", p->tot_len, next_hdr_offset)); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet")); goto free_and_return; } else { /* pass to IP layer */ ip4_input(p, netif); } break; case PP_HTONS(ETHTYPE_ARP): if (!(netif->flags & NETIF_FLAG_ETHARP)) { goto free_and_return; } /* skip Ethernet header */ if ((p->len < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ethernet_input: ARP response packet dropped, too short (%"U16_F"/%"U16_F")\n", p->tot_len, next_hdr_offset)); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet")); ETHARP_STATS_INC(etharp.lenerr); ETHARP_STATS_INC(etharp.drop); goto free_and_return; } else { /* pass p to ARP module */ etharp_input(p, netif); } break; #endif /* LWIP_IPV4 && LWIP_ARP */ #if PPPOE_SUPPORT case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */ pppoe_disc_input(netif, p); break; case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */ pppoe_data_input(netif, p); break; #endif /* PPPOE_SUPPORT */ #if LWIP_IPV6 case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */ /* skip Ethernet header */ if ((p->len < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ethernet_input: IPv6 packet dropped, too short (%"U16_F"/%"U16_F")\n", p->tot_len, next_hdr_offset)); goto free_and_return; } else { /* pass to IPv6 layer */ ip6_input(p, netif); } break; #endif /* LWIP_IPV6 */ default: #ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL if (LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) { break; } #endif ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); MIB2_STATS_NETIF_INC(netif, ifinunknownprotos); goto free_and_return; } /* This means the pbuf is freed or consumed, so the caller doesn't have to free it again */ return ERR_OK; free_and_return: pbuf_free(p); return ERR_OK; } /** * @ingroup ethernet * Send an ethernet packet on the network using netif->linkoutput(). * The ethernet header is filled in before sending. * * @see LWIP_HOOK_VLAN_SET * * @param netif the lwIP network interface on which to send the packet * @param p the packet to send. pbuf layer must be @ref PBUF_LINK. * @param src the source MAC address to be copied into the ethernet header * @param dst the destination MAC address to be copied into the ethernet header * @param eth_type ethernet type (@ref lwip_ieee_eth_type) * @return ERR_OK if the packet was sent, any other err_t on failure */ err_t ethernet_output(struct netif * netif, struct pbuf * p, const struct eth_addr * src, const struct eth_addr * dst, u16_t eth_type) { struct eth_hdr *ethhdr; u16_t eth_type_be = lwip_htons(eth_type); #if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) s32_t vlan_prio_vid = LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type); if (vlan_prio_vid >= 0) { struct eth_vlan_hdr *vlanhdr; LWIP_ASSERT("prio_vid must be <= 0xFFFF", vlan_prio_vid <= 0xFFFF); if (pbuf_add_header(p, SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) != 0) { goto pbuf_header_failed; } vlanhdr = (struct eth_vlan_hdr *)(((u8_t *)p->payload) + SIZEOF_ETH_HDR); vlanhdr->tpid = eth_type_be; vlanhdr->prio_vid = lwip_htons((u16_t)vlan_prio_vid); eth_type_be = PP_HTONS(ETHTYPE_VLAN); } else #endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ { if (pbuf_add_header(p, SIZEOF_ETH_HDR) != 0) { goto pbuf_header_failed; } } LWIP_ASSERT_CORE_LOCKED(); ethhdr = (struct eth_hdr *)p->payload; ethhdr->type = eth_type_be; SMEMCPY(ðhdr->dest, dst, ETH_HWADDR_LEN); SMEMCPY(ðhdr->src, src, ETH_HWADDR_LEN); LWIP_ASSERT("netif->hwaddr_len must be 6 for ethernet_output!", (netif->hwaddr_len == ETH_HWADDR_LEN)); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethernet_output: sending packet %p\n", (void *)p)); /* send the packet */ return netif->linkoutput(netif, p); pbuf_header_failed: LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("ethernet_output: could not allocate room for header.\n")); LINK_STATS_INC(link.lenerr); return ERR_BUF; }