/** * New proxied UDP conversation created. * Global callback for udp_proxy_accept(). */ static void pxudp_pcb_accept(void *arg, struct udp_pcb *newpcb, struct pbuf *p, ip_addr_t *addr, u16_t port) { struct pxudp *pxudp; ipX_addr_t dst_addr; int mapping; int sdom; SOCKET sock; LWIP_ASSERT1(newpcb != NULL); LWIP_ASSERT1(p != NULL); LWIP_UNUSED_ARG(arg); pxudp = pxudp_allocate(); if (pxudp == NULL) { DPRINTF(("pxudp_allocate: failed\n")); udp_remove(newpcb); pbuf_free(p); return; } sdom = PCB_ISIPV6(newpcb) ? PF_INET6 : PF_INET; mapping = pxremap_outbound_ipX(PCB_ISIPV6(newpcb), &dst_addr, &newpcb->local_ip); #if 0 /* XXX: DNS IPv6->IPv4 remapping hack */ if (mapping == PXREMAP_MAPPED && newpcb->local_port == 53 && PCB_ISIPV6(newpcb)) { /* * "Remap" DNS over IPv6 to IPv4 since Ubuntu dnsmasq does not * listen on IPv6. */ sdom = PF_INET; ipX_addr_set_loopback(0, &dst_addr); } #endif /* DNS IPv6->IPv4 remapping hack */ sock = proxy_connected_socket(sdom, SOCK_DGRAM, &dst_addr, newpcb->local_port); if (sock == INVALID_SOCKET) { udp_remove(newpcb); pbuf_free(p); return; } pxudp->sock = sock; pxudp->pcb = newpcb; udp_recv(newpcb, pxudp_pcb_recv, pxudp); pxudp->pmhdl.callback = pxudp_pmgr_pump; pxudp_chan_send(POLLMGR_CHAN_PXUDP_ADD, pxudp); /* dispatch directly instead of calling pxudp_pcb_recv() */ pxudp_pcb_forward_outbound(pxudp, p, addr, port); }
/** * Return a connection's local or remote address * Called from netconn_getaddr * * @param msg the api_msg_msg pointing to the connection */ void lwip_netconn_do_getaddr(struct api_msg_msg *msg) { if (msg->conn->pcb.ip != NULL) { if (msg->msg.ad.local) { ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), API_EXPR_DEREF(msg->msg.ad.ipaddr), msg->conn->pcb.ip->local_ip); } else { ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), API_EXPR_DEREF(msg->msg.ad.ipaddr), msg->conn->pcb.ip->remote_ip); } msg->err = ERR_OK; switch (NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: if (msg->msg.ad.local) { API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol; } else { /* return an error as connecting is only a helper for upper layers */ msg->err = ERR_CONN; } break; #endif /* LWIP_RAW */ #if LWIP_UDP case NETCONN_UDP: if (msg->msg.ad.local) { API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port; } else { if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) { msg->err = ERR_CONN; } else { API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port; } } break; #endif /* LWIP_UDP */ #if LWIP_TCP case NETCONN_TCP: if ((msg->msg.ad.local == 0) && ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) { /* pcb is not connected and remote name is requested */ msg->err = ERR_CONN; } else { API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port); } break; #endif /* LWIP_TCP */ default: LWIP_ASSERT("invalid netconn_type", 0); break; } } else { msg->err = ERR_CONN; } TCPIP_APIMSG_ACK(msg); }
/** * Send some data on a RAW or UDP pcb contained in a netconn * Called from netconn_send * * @param msg the api_msg_msg pointing to the connection */ void lwip_netconn_do_send(struct api_msg_msg *msg) { if (ERR_IS_FATAL(msg->conn->last_err)) { msg->err = msg->conn->last_err; } else { msg->err = ERR_CONN; if (msg->conn->pcb.tcp != NULL) { switch (NETCONNTYPE_GROUP(msg->conn->type)) { #if LWIP_RAW case NETCONN_RAW: if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p); } else { msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr)); } break; #endif #if LWIP_UDP case NETCONN_UDP: #if LWIP_CHECKSUM_ON_COPY if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p, msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); } else { msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port, msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum); } #else /* LWIP_CHECKSUM_ON_COPY */ if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) { msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); } else { msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port); } #endif /* LWIP_CHECKSUM_ON_COPY */ break; #endif /* LWIP_UDP */ default: break; } } } TCPIP_APIMSG_ACK(msg); }
static void pxdns_query(struct pxdns *pxdns, struct udp_pcb *pcb, struct pbuf *p, ipX_addr_t *addr, u16_t port) { struct request *req; int sent; if (pxdns->nresolvers == 0) { /* nothing we can do */ pbuf_free(p); return; } req = calloc(1, sizeof(struct request) - 1 + p->tot_len); if (req == NULL) { pbuf_free(p); return; } /* copy request data */ req->size = p->tot_len; pbuf_copy_partial(p, req->data, p->tot_len, 0); /* save client identity and client's request id */ req->pcb = pcb; ipX_addr_copy(PCB_ISIPV6(pcb), req->client_addr, *addr); req->client_port = port; memcpy(&req->client_id, req->data, sizeof(req->client_id)); /* slap our request id onto it */ req->id = pxdns->id++; memcpy(req->data, &req->id, sizeof(u16_t)); /* resolver to forward to */ req->generation = pxdns->generation; req->residx = 0; /* prepare for relaying the reply back to guest */ req->msg_reply.type = TCPIP_MSG_CALLBACK_STATIC; req->msg_reply.sem = NULL; req->msg_reply.msg.cb.function = pxdns_pcb_reply; req->msg_reply.msg.cb.ctx = (void *)req; DPRINTF2(("%s: req=%p: client id %d -> id %d\n", __func__, (void *)req, req->client_id, req->id)); pxdns_request_register(pxdns, req); sent = pxdns_forward_outbound(pxdns, req); if (!sent) { sent = pxdns_rexmit(pxdns, req); } if (!sent) { pxdns_request_deregister(pxdns, req); pxdns_request_free(req); } }
/** * Receive callback function for RAW netconns. * Doesn't 'eat' the packet, only references it and sends it to * conn->recvmbox * * @see raw.h (struct raw_pcb.recv) for parameters and return value */ static u8_t recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *addr) { struct pbuf *q; struct netbuf *buf; struct netconn *conn; LWIP_UNUSED_ARG(addr); conn = (struct netconn *)arg; if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) { #if LWIP_SO_RCVBUF int recv_avail; SYS_ARCH_GET(conn->recv_avail, recv_avail); if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) { return 0; } #endif /* LWIP_SO_RCVBUF */ /* copy the whole packet into new pbufs */ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if(q != NULL) { if (pbuf_copy(q, p) != ERR_OK) { pbuf_free(q); q = NULL; } } if (q != NULL) { u16_t len; buf = (struct netbuf *)memp_malloc(MEMP_NETBUF); if (buf == NULL) { pbuf_free(q); return 0; } buf->p = q; buf->ptr = q; ipX_addr_copy(PCB_ISIPV6(pcb), buf->addr, *ipX_current_src_addr()); buf->port = pcb->protocol; len = q->tot_len; if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { netbuf_delete(buf); return 0; } else { #if LWIP_SO_RCVBUF SYS_ARCH_INC(conn->recv_avail, len); #endif /* LWIP_SO_RCVBUF */ /* Register event with callback */ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len); } } } return 0; /* do not eat the packet */ }
/** * Join multicast groups for UDP netconns. * Called from netconn_join_leave_group * * @param msg the api_msg_msg pointing to the connection */ void lwip_netconn_do_join_leave_group(struct api_msg_msg *msg) { if (ERR_IS_FATAL(msg->conn->last_err)) { msg->err = msg->conn->last_err; } else { if (msg->conn->pcb.tcp != NULL) { if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { #if LWIP_UDP #if LWIP_IPV6 && LWIP_IPV6_MLD if (PCB_ISIPV6(msg->conn->pcb.udp)) { if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { msg->err = mld6_joingroup(ipX_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)), ipX_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr))); } else { msg->err = mld6_leavegroup(ipX_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)), ipX_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr))); } } else #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ { #if LWIP_IGMP if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { msg->err = igmp_joingroup(ipX_2_ip(API_EXPR_REF(msg->msg.jl.netif_addr)), ipX_2_ip(API_EXPR_REF(msg->msg.jl.multiaddr))); } else { msg->err = igmp_leavegroup(ipX_2_ip(API_EXPR_REF(msg->msg.jl.netif_addr)), ipX_2_ip(API_EXPR_REF(msg->msg.jl.multiaddr))); } #endif /* LWIP_IGMP */ } #endif /* LWIP_UDP */ #if (LWIP_TCP || LWIP_RAW) } else { msg->err = ERR_VAL; #endif /* (LWIP_TCP || LWIP_RAW) */ } } else { msg->err = ERR_CONN; } } TCPIP_APIMSG_ACK(msg); }
/** * Connect an RAW PCB. This function is required by upper layers * of lwip. Using the raw api you could use raw_sendto() instead * * This will associate the RAW PCB with the remote address. * * @param pcb RAW PCB to be connected with remote address ipaddr and port. * @param ipaddr remote IP address to connect with. * * @return lwIP error code * * @see raw_disconnect() and raw_sendto() */ err_t raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr) { ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); return ERR_OK; }
/** * Lwip thread callback invoked via fwudp::msg_send */ void fwudp_pcb_send(void *arg) { struct fwudp *fwudp = (struct fwudp *)arg; struct fwudp_dgram dgram; struct udp_pcb *pcb; struct udp_pcb **pprev; int isv6; size_t idx; idx = fwudp->inbuf.unsent; if (idx == fwudp->inbuf.vacant) { /* empty buffer - shouldn't happen! */ DPRINTF(("%s: ring buffer empty!\n", __func__)); return; } dgram = fwudp->inbuf.buf[idx]; /* struct copy */ #if 1 /* valgrind hint */ fwudp->inbuf.buf[idx].p = NULL; #endif if (++idx == fwudp->inbuf.bufsize) { idx = 0; } fwudp->inbuf.unsent = idx; /* XXX: this is *STUPID* */ isv6 = (fwudp->fwspec.sdom == PF_INET6); pprev = &udp_proxy_pcbs; for (pcb = udp_proxy_pcbs; pcb != NULL; pcb = pcb->next) { if (PCB_ISIPV6(pcb) == isv6 && pcb->remote_port == fwudp->dst_port && ipX_addr_cmp(isv6, &fwudp->dst_addr, &pcb->remote_ip) && pcb->local_port == dgram.src_port && ipX_addr_cmp(isv6, &dgram.src_addr, &pcb->local_ip)) { break; } else { pprev = &pcb->next; } } if (pcb != NULL) { *pprev = pcb->next; pcb->next = udp_proxy_pcbs; udp_proxy_pcbs = pcb; /* * XXX: check that its ours and not accidentally created by * outbound traffic. * * ???: Otherwise? Expire it and set pcb = NULL; to create a * new one below? */ } if (pcb == NULL) { pcb = udp_new(); if (pcb == NULL) { goto out; } ip_set_v6(pcb, isv6); /* equivalent of udp_bind */ ipX_addr_set(isv6, &pcb->local_ip, &dgram.src_addr); pcb->local_port = dgram.src_port; /* equivalent to udp_connect */ ipX_addr_set(isv6, &pcb->remote_ip, &fwudp->dst_addr); pcb->remote_port = fwudp->dst_port; pcb->flags |= UDP_FLAGS_CONNECTED; udp_recv(pcb, fwudp_pcb_recv, fwudp); pcb->next = udp_proxy_pcbs; udp_proxy_pcbs = pcb; udp_proxy_timer_needed(); } udp_send(pcb, dgram.p); out: pbuf_free(dgram.p); }
/** * Determine if in incoming IP packet is covered by a RAW PCB * and if so, pass it to a user-provided receive callback function. * * Given an incoming IP datagram (as a chain of pbufs) this function * finds a corresponding RAW PCB and calls the corresponding receive * callback function. * * @param p pbuf to be demultiplexed to a RAW PCB. * @param inp network interface on which the datagram was received. * @return - 1 if the packet has been eaten by a RAW PCB receive * callback function. The caller MAY NOT not reference the * packet any longer, and MAY NOT call pbuf_free(). * @return - 0 if packet is not eaten (pbuf is still referenced by the * caller). * */ u8_t ICACHE_FLASH_ATTR raw_input(struct pbuf *p, struct netif *inp) { struct raw_pcb *pcb, *prev; struct ip_hdr *iphdr; s16_t proto; u8_t eaten = 0; #if LWIP_IPV6 struct ip6_hdr *ip6hdr; #endif /* LWIP_IPV6 */ LWIP_UNUSED_ARG(inp); iphdr = (struct ip_hdr *)p->payload; #if LWIP_IPV6 if (IPH_V(iphdr) == 6) { ip6hdr = (struct ip6_hdr *)p->payload; proto = IP6H_NEXTH(ip6hdr); } else #endif /* LWIP_IPV6 */ { proto = IPH_PROTO(iphdr); } prev = NULL; pcb = raw_pcbs; /* loop through all raw pcbs until the packet is eaten by one */ /* this allows multiple pcbs to match against the packet by design */ while ((eaten == 0) && (pcb != NULL)) { if ((pcb->protocol == proto) && IP_PCB_IPVER_INPUT_MATCH(pcb) && (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip) || ipX_addr_cmp(PCB_ISIPV6(pcb), &(pcb->local_ip), ipX_current_dest_addr()))) { #if IP_SOF_BROADCAST_RECV /* broadcast filter? */ if ((ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(ip_current_dest_addr(), inp)) #if LWIP_IPV6 && !PCB_ISIPV6(pcb) #endif /* LWIP_IPV6 */ ) #endif /* IP_SOF_BROADCAST_RECV */ { /* receive callback function available? */ if (pcb->recv.ip4 != NULL) { #ifndef LWIP_NOASSERT void* old_payload = p->payload; #endif /* the receive callback function did not eat the packet? */ eaten = pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr()); if (eaten != 0) { /* receive function ate the packet */ p = NULL; eaten = 1; if (prev != NULL) { /* move the pcb to the front of raw_pcbs so that is found faster next time */ prev->next = pcb->next; pcb->next = raw_pcbs; raw_pcbs = pcb; } } else { /* sanity-check that the receive callback did not alter the pbuf */ LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet", p->payload == old_payload); } } /* no receive callback function was set for this raw PCB */ } /* drop the packet */ } prev = pcb; pcb = pcb->next; } return eaten; }
/** * Send the raw IP packet to the given address. Note that actually you cannot * modify the IP headers (this is inconsistent with the receive callback where * you actually get the IP headers), you can only specify the IP payload here. * It requires some more changes in lwIP. (there will be a raw_send() function * then.) * * @param pcb the raw pcb which to send * @param p the IP payload to send * @param ipaddr the destination address of the IP packet * */ err_t raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr) { err_t err; struct netif *netif; ip_addr_t *src_ip; struct pbuf *q; /* q will be sent down the stack */ s16_t header_size; const ip_addr_t *dst_ip = ipaddr; #if LWIP_IPV4 && LWIP_IPV6 ip_addr_t src_ip_tmp; #endif /* LWIP_IPV4 && LWIP_IPV6 */ if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) { return ERR_VAL; } LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); header_size = ( #if LWIP_IPV4 && LWIP_IPV6 PCB_ISIPV6(pcb) ? IP6_HLEN : IP_HLEN); #elif LWIP_IPV4 IP_HLEN); #else IP6_HLEN); #endif /* not enough space to add an IP header to first pbuf in given p chain? */ if (pbuf_header(p, header_size)) { /* allocate header in new pbuf */ q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); /* new header pbuf could not be allocated? */ if (q == NULL) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); return ERR_MEM; } if (p->tot_len != 0) { /* chain header q in front of given pbuf p */ pbuf_chain(q, p); } /* { first pbuf q points to header pbuf } */ LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); } else { /* first pbuf q equals given pbuf */ q = p; if(pbuf_header(q, -header_size)) { LWIP_ASSERT("Can't restore header we just removed!", 0); return ERR_MEM; } } netif = ip_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip); if (netif == NULL) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to ")); ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip); /* free any temporary header pbuf allocated by pbuf_header() */ if (q != p) { pbuf_free(q); } return ERR_RTE; } #if IP_SOF_BROADCAST #if LWIP_IPV6 if (!PCB_ISIPV6(pcb)) #endif /* LWIP_IPV6 */ { /* broadcast filter? */ if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); /* free any temporary header pbuf allocated by pbuf_header() */ if (q != p) { pbuf_free(q); } return ERR_VAL; } } #endif /* IP_SOF_BROADCAST */ if (ip_addr_isany(&pcb->local_ip)) { /* use outgoing network interface IP address as source address */ src_ip = ip_netif_get_local_ip(PCB_ISIPV6(pcb), netif, dst_ip, &src_ip_tmp); #if LWIP_IPV6 if (src_ip == NULL) { if (q != p) { pbuf_free(q); } return ERR_RTE; } #endif /* LWIP_IPV6 */ } else { /* use RAW PCB local IP address as source address */ src_ip = &pcb->local_ip; } #if LWIP_IPV6 /* If requested, based on the IPV6_CHECKSUM socket option per RFC3542, compute the checksum and update the checksum in the payload. */ if (PCB_ISIPV6(pcb) && pcb->chksum_reqd) { u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(dst_ip)); LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2)); SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t)); } #endif NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); err = ip_output_if(PCB_ISIPV6(pcb), q, src_ip, dst_ip, pcb->ttl, pcb->tos, pcb->protocol, netif); NETIF_SET_HWADDRHINT(netif, NULL); /* did we chain a header earlier? */ if (q != p) { /* free the header */ pbuf_free(q); } return err; }
/** * Connect an RAW PCB. This function is required by upper layers * of lwip. Using the raw api you could use raw_sendto() instead * * This will associate the RAW PCB with the remote address. * * @param pcb RAW PCB to be connected with remote address ipaddr and port. * @param ipaddr remote IP address to connect with. * * @return lwIP error code * * @see raw_disconnect() and raw_sendto() */ err_t ICACHE_FLASH_ATTR raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr) { ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); return ERR_OK; }
/** * Send the raw IP packet to the given address. Note that actually you cannot * modify the IP headers (this is inconsistent with the receive callback where * you actually get the IP headers), you can only specify the IP payload here. * It requires some more changes in lwIP. (there will be a raw_send() function * then.) * * @param pcb the raw pcb which to send * @param p the IP payload to send * @param ipaddr the destination address of the IP packet * */ err_t ICACHE_FLASH_ATTR raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) { err_t err; struct netif *netif; ipX_addr_t *src_ip; struct pbuf *q; /* q will be sent down the stack */ s16_t header_size; ipX_addr_t *dst_ip = ip_2_ipX(ipaddr); LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); header_size = ( #if LWIP_IPV6 PCB_ISIPV6(pcb) ? IP6_HLEN : #endif /* LWIP_IPV6 */ IP_HLEN); /* not enough space to add an IP header to first pbuf in given p chain? */ if (pbuf_header(p, header_size)) { /* allocate header in new pbuf */ q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); /* new header pbuf could not be allocated? */ if (q == NULL) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); return ERR_MEM; } if (p->tot_len != 0) { /* chain header q in front of given pbuf p */ pbuf_chain(q, p); } /* { first pbuf q points to header pbuf } */ LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); } else { /* first pbuf q equals given pbuf */ q = p; if(pbuf_header(q, -header_size)) { LWIP_ASSERT("Can't restore header we just removed!", 0); return ERR_MEM; } } netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip); if (netif == NULL) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to ")); ipX_addr_debug_print(PCB_ISIPV6(pcb), RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip); /* free any temporary header pbuf allocated by pbuf_header() */ if (q != p) { pbuf_free(q); } return ERR_RTE; } #if IP_SOF_BROADCAST #if LWIP_IPV6 /* @todo: why does IPv6 not filter broadcast with SOF_BROADCAST enabled? */ if (!PCB_ISIPV6(pcb)) #endif /* LWIP_IPV6 */ { /* broadcast filter? */ if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); /* free any temporary header pbuf allocated by pbuf_header() */ if (q != p) { pbuf_free(q); } return ERR_VAL; } } #endif /* IP_SOF_BROADCAST */ if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { /* use outgoing network interface IP address as source address */ src_ip = ipX_netif_get_local_ipX(PCB_ISIPV6(pcb), netif, dst_ip); #if LWIP_IPV6 if (src_ip == NULL) { if (q != p) { pbuf_free(q); } return ERR_RTE; } #endif /* LWIP_IPV6 */ } else { /* use RAW PCB local IP address as source address */ src_ip = &pcb->local_ip; } NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); err = ipX_output_if(PCB_ISIPV6(pcb), q, ipX_2_ip(src_ip), ipX_2_ip(dst_ip), pcb->ttl, pcb->tos, pcb->protocol, netif); NETIF_SET_HWADDRHINT(netif, NULL); /* did we chain a header earlier? */ if (q != p) { /* free the header */ pbuf_free(q); } return err; }
/** * Bind a RAW PCB. * * @param pcb RAW PCB to be bound with a local address ipaddr. * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to * bind to all local interfaces. * * @return lwIP error code. * - ERR_OK. Successful. No error occured. * - ERR_USE. The specified IP address is already bound to by * another RAW PCB. * * @see raw_disconnect() */ err_t ICACHE_FLASH_ATTR raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr) { ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); return ERR_OK; }
static term_t ol_tcp_control(outlet_t *ol, uint32_t op, uint8_t *data, int dlen, term_t reply_to, heap_t *hp) { char rbuf[256]; char *reply = rbuf; int sz; assert(ol != 0); assert(ol->tcp != 0 || op == INET_REQ_OPEN || op == INET_REQ_SUBSCRIBE); switch (op) { case INET_REQ_OPEN: { if (dlen != 2 || data[1] != INET_TYPE_STREAM) goto error; uint8_t family = data[0]; if (family != INET_AF_INET && family != INET_AF_INET6) goto error; assert(ol->tcp == 0); #if LWIP_IPV6 ol->tcp = (family == INET_AF_INET6) ?tcp_new_ip6() :tcp_new(); #else if (family != INET_AF_INET) goto error; ol->tcp = tcp_new(); #endif assert(ol->tcp != 0); // see comment in ol_tcp_animate() tcp_setprio(ol->tcp, TCP_PRIO_MAX +1); tcp_arg(ol->tcp, ol); // callback arg tcp_recv(ol->tcp, recv_cb); tcp_sent(ol->tcp, sent_cb); tcp_err(ol->tcp, error_cb); *reply++ = INET_REP_OK; } break; case INET_REQ_CONNECT: { int is_ipv6 = PCB_ISIPV6(ol->tcp); if ((is_ipv6 && dlen != 4 +2 +16) || (!is_ipv6 && dlen != 4 +2 +4)) goto error; uint32_t timeout = GET_UINT_32(data); uint16_t remote_port = GET_UINT_16(data +4); err_t err; if (!is_ipv6) { ip_addr_t where_to; where_to.addr = ntohl(GET_UINT_32(data +4 +2)); err = tcp_connect(ol->tcp, &where_to, remote_port, connected_cb); } else { #if LWIP_IPV6 ip6_addr_t where_to; where_to.addr[0] = ntohl(GET_UINT_32(data +4 +2)); where_to.addr[1] = ntohl(GET_UINT_32(data +4 +2 +4)); where_to.addr[2] = ntohl(GET_UINT_32(data +4 +2 +8)); where_to.addr[3] = ntohl(GET_UINT_32(data +4 +2 +12)); err = tcp_connect_ip6(ol->tcp, &where_to, remote_port, connected_cb); #else goto error; #endif } // Does it make connections faster? tcp_output(ol->tcp); if (err == ERR_OK) { cr_defer_reply(ol, reply_to, timeout); *reply++ = INET_REP_OK; uint16_t ref = ASYNC_REF; // Why this is needed? A constant will do. PUT_UINT_16(reply, ref); reply += 2; } else { // //TODO: ERR_RTE possible too (IPv6) // assert(err == ERR_MEM); REPLY_INET_ERROR("enomem"); } } break; case INET_REQ_PEER: if (ol->tcp->state == CLOSED) REPLY_INET_ERROR("enotconn"); else { *reply++ = INET_REP_OK; *reply++ = INET_AF_INET; uint16_t peer_port = ol->tcp->remote_port; PUT_UINT_16(reply, peer_port); reply += 2; if (PCB_ISIPV6(ol->tcp)) { ip_addr_set_hton((ip_addr_t *)reply, (ip_addr_t *)&ol->tcp->remote_ip); reply += 4; } else { #if LWIP_IPV6 ip6_addr_set_hton((ip6_addr_t *)reply, (ip6_addr_t *)&ol->tcp->remote_ip); reply += 16; #else goto error; #endif } } break; case INET_REQ_NAME: if (ol->tcp->state == CLOSED) REPLY_INET_ERROR("enotconn"); else { *reply++ = INET_REP_OK; int is_ipv6 = PCB_ISIPV6(ol->tcp); *reply++ = (is_ipv6) ?INET_AF_INET6 :INET_AF_INET; uint16_t name_port = ol->tcp->local_port; PUT_UINT_16(reply, name_port); reply += 2; if (PCB_ISIPV6(ol->tcp)) { ip_addr_set_hton((ip_addr_t *)reply, (ip_addr_t *)&ol->tcp->local_ip); reply += 4; } else { #if LWIP_IPV6 ip6_addr_set_hton((ip6_addr_t *)reply, (ip6_addr_t *)&ol->tcp->local_ip); reply += 16; #else goto error; #endif } } break; case INET_REQ_BIND: { int is_ipv6 = PCB_ISIPV6(ol->tcp); if ((is_ipv6 && dlen != 2 +16) || (!is_ipv6 && dlen != 2 +4)) goto error; uint16_t port = GET_UINT_16(data); if (!is_ipv6) { ip_addr_t addr; addr.addr = ntohl(GET_UINT_32(data +2)); tcp_bind(ol->tcp, &addr, port); // always succeeds } else { #if LWIP_IPV6 ip6_addr_t addr; addr.addr[0] = ntohl(GET_UINT_32(data +2)); addr.addr[1] = ntohl(GET_UINT_32(data +2 +4)); addr.addr[2] = ntohl(GET_UINT_32(data +2 +8)); addr.addr[3] = ntohl(GET_UINT_32(data +2 +12)); tcp_bind_ip6(ol->tcp, &addr, port); // always succeeds #else goto error; #endif } uint16_t local_port = ol->tcp->local_port; *reply++ = INET_REP_OK; PUT_UINT_16(reply, local_port); reply += 2; } break; case INET_REQ_LISTEN: { assert(ol->recv_buf_node == 0); // or use destroy_private() int backlog = GET_UINT_16(data); ol_tcp_acc_promote(ol, ol->tcp, backlog); *reply++ = INET_REP_OK; } break; case INET_REQ_SETOPTS: if (ol_tcp_set_opts(ol, data, dlen) < 0) goto error; *reply++ = INET_REP_OK; break; case INET_REQ_GETOPTS: sz = ol_tcp_get_opts(ol, data, dlen, rbuf+1, sizeof(rbuf) -1); if (sz < 0) goto error; *reply++ = INET_REP_OK; reply += sz; break; case INET_REQ_GETSTAT: // // lwIP can provide some of the statistics but not all // REPLY_INET_ERROR("enotsup"); break; case INET_REQ_SUBSCRIBE: if (dlen != 1 && data[0] != INET_SUBS_EMPTY_OUT_Q) goto error; if (ol->empty_queue_in_progress) goto error; //TODO: allow multiple subscriptions int qlen = tcp_sndqueuelen(ol->tcp); if (qlen > 0) { ol->empty_queue_in_progress = 1; ol->empty_queue_reply_to = reply_to; } *reply++ = INET_REP_OK; *reply++ = INET_SUBS_EMPTY_OUT_Q; PUT_UINT_32(reply, qlen); reply += 4; break; case TCP_REQ_RECV: if (dlen != 4 +4) goto error; uint32_t msecs = GET_UINT_32(data); uint32_t recv_num = GET_UINT_32(data +4); if (ol->active != INET_PASSIVE) goto error; if (ol->packet == TCP_PB_RAW && recv_num > ol->recv_bufsize) goto error; if (ol->peer_close_detected) inet_async_error(ol->oid, reply_to, ASYNC_REF, A_CLOSED); else { cr_defer_reply(ol, reply_to, msecs); if (ol->packet == TCP_PB_RAW) ol->recv_expected_size = recv_num; // Enough data may have already been buffered proc_t *cont_proc = scheduler_lookup(reply_to); assert(cont_proc != 0); if (recv_bake_packets(ol, cont_proc) < 0) goto error; } *reply++ = INET_REP_OK; uint16_t my_ref = ASYNC_REF; PUT_UINT_16(reply, my_ref); reply += 2; break; case TCP_REQ_SHUTDOWN: if (dlen != 1) goto error; uint8_t what = data[0]; // 0 - read // 1 - write // 2 - read_write int shut_rx = (what == 0) || (what == 2); int shut_tx = (what == 1) || (what == 2); if (ol->tcp->state == LISTEN) REPLY_INET_ERROR("enotconn"); else { tcp_shutdown(ol->tcp, shut_rx, shut_tx); // TODO: return code ignored *reply++ = INET_REP_OK; } break; default: error: REPLY_INET_ERROR("einval"); } int rlen = reply -rbuf; assert(rlen >= 1 && rlen <= sizeof(rbuf)); term_t result = heap_str_N(hp, rbuf, rlen); if (result == noval) return A_NO_MEMORY; return result; }
/** * Bind a RAW PCB. * * @param pcb RAW PCB to be bound with a local address ipaddr. * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to * bind to all local interfaces. * * @return lwIP error code. * - ERR_OK. Successful. No error occurred. * - ERR_USE. The specified IP address is already bound to by * another RAW PCB. * * @see raw_disconnect() */ err_t raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr) { ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); return ERR_OK; }
/** * Sends an generic or enterprise specific trap message. * * @param generic_trap is the trap code * @param eoid points to enterprise object identifier * @param specific_trap used for enterprise traps when generic_trap == 6 * @return ERR_OK when success, ERR_MEM if we're out of memory * * @note the caller is responsible for filling in outvb in the trap_msg * @note the use of the enterprise identifier field * is per RFC1215. * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps * and .iso.org.dod.internet.private.enterprises.yourenterprise * (sysObjectID) for specific traps. */ err_t snmp_send_trap(s8_t generic_trap, const struct snmp_obj_id *eoid, s32_t specific_trap) { struct snmp_trap_dst *td; struct netif *dst_if; const ip_addr_t* dst_ip; struct pbuf *p; u16_t i,tot_len; err_t err = ERR_OK; for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) { if ((td->enable != 0) && !ip_addr_isany(&td->dip)) { /* network order trap destination */ ip_addr_copy(trap_msg.dip, td->dip); /* lookup current source address for this dst */ ip_route_get_local_ip(PCB_ISIPV6(trap_msg.pcb), &trap_msg.pcb->local_ip, &td->dip, dst_if, dst_ip); if ((dst_if != NULL) && (dst_ip != NULL)) { trap_msg.sip_raw_len = (IP_IS_V6_VAL(*dst_ip) ? 16 : 4); memcpy(trap_msg.sip_raw, dst_ip, trap_msg.sip_raw_len); trap_msg.gen_trap = generic_trap; trap_msg.spc_trap = specific_trap; if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) { /* enterprise-Specific trap */ trap_msg.enterprise = eoid; } else { /* generic (MIB-II) trap */ mib2_get_snmpgrpid_ptr(&trap_msg.enterprise); } MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts); /* pass 0, calculate length fields */ tot_len = snmp_varbind_list_sum(&trap_msg.outvb); tot_len = snmp_trap_header_sum(&trap_msg, tot_len); /* allocate pbuf(s) */ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM); if (p != NULL) { u16_t ofs; /* pass 1, encode packet ino the pbuf(s) */ ofs = snmp_trap_header_enc(&trap_msg, p); snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); mib2_inc_snmpouttraps(); mib2_inc_snmpoutpkts(); /** send to the TRAP destination */ udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT); pbuf_free(p); } else { err = ERR_MEM; } } else { /* routing error */ err = ERR_RTE; } } } return err; }
/****************************************************************************** * FunctionName : espconn_udp_sendto * Description : sent data for UDP * Parameters : void *arg -- UDP to send * uint8* psent -- Data to send * uint16 length -- Length of data to send * Returns : return espconn error code. * - ESPCONN_OK. Successful. No error occured. * - ESPCONN_MEM. Out of memory. * - ESPCONN_RTE. Could not find route to destination address. * - More errors could be returned by lower protocol layers. *******************************************************************************/ err_t ICACHE_FLASH_ATTR espconn_udp_sendto(void* arg, uint8* psent, uint16 length) { espconn_msg* pudp_sent = arg; struct udp_pcb* upcb = pudp_sent->pcommon.pcb; struct espconn* pespconn = pudp_sent->pespconn; struct pbuf* p, *q , *p_temp; struct ip_addr dst_ip; u16_t dst_port; u8_t* data = NULL; u16_t cnt = 0; u16_t datalen = 0; u16_t i = 0; err_t err; LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %d %p\n", __LINE__, length, upcb)); if (pudp_sent == NULL || upcb == NULL || psent == NULL || length == 0) { return ESPCONN_ARG; } if ((IP_MAX_MTU - 20 - 8) < length) { datalen = IP_MAX_MTU - 20 - 8; } else { datalen = length; } p = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM); LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, p)); if (p != NULL) { q = p; while (q != NULL) { data = (u8_t*)q->payload; LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %p\n", __LINE__, data)); for (i = 0; i < q->len; i++) { data[i] = ((u8_t*) psent)[cnt++]; } q = q->next; } } else { return ESPCONN_MEM; } dst_port = pespconn->proto.udp->remote_port; IP4_ADDR(&dst_ip, pespconn->proto.udp->remote_ip[0], pespconn->proto.udp->remote_ip[1], pespconn->proto.udp->remote_ip[2], pespconn->proto.udp->remote_ip[3]); LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sent %d %x %d\n", __LINE__, upcb->remote_ip, upcb->remote_port)); #if LWIP_IPV6 || LWIP_IGMP ipX_addr_t* dst_ip_route = ip_2_ipX(&dst_ip); if (ipX_addr_ismulticast(PCB_ISIPV6(upcb), dst_ip_route)) { /* For multicast, find a netif based on source address. */ #if LWIP_IPV6 if (PCB_ISIPV6(upcb)) { dst_ip_route = &upcb->local_ip; } else #endif /* LWIP_IPV6 */ { #if LWIP_IGMP upcb->multicast_ip.addr = dst_ip.addr; #endif /* LWIP_IGMP */ } } #endif /* LWIP_IPV6 || LWIP_IGMP */ struct netif* sta_netif = (struct netif*)eagle_lwip_getif(0x00); struct netif* ap_netif = (struct netif*)eagle_lwip_getif(0x01); if (wifi_get_opmode() == ESPCONN_AP_STA && default_interface == ESPCONN_AP_STA && sta_netif != NULL && ap_netif != NULL) { if (netif_is_up(sta_netif) && netif_is_up(ap_netif) && \ ip_addr_isbroadcast(&(upcb->remote_ip.ip4), sta_netif) && \ ip_addr_isbroadcast(&(upcb->remote_ip.ip4), ap_netif)) { p_temp = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM); if (pbuf_copy(p_temp, p) != ERR_OK) { LWIP_DEBUGF(ESPCONN_UDP_DEBUG, ("espconn_udp_sendto: copying to new pbuf failed\n")); return ESPCONN_ARG; } netif_set_default(sta_netif); err = udp_sendto(upcb, p_temp, &dst_ip, dst_port); pbuf_free(p_temp); netif_set_default(ap_netif); } } err = udp_sendto(upcb, p, &dst_ip, dst_port); if (p->ref != 0) { pbuf_free(p); pudp_sent->pcommon.ptrbuf = psent + datalen; pudp_sent->pcommon.cntr = length - datalen; pudp_sent->pcommon.err = err; espconn_data_sent(pudp_sent, ESPCONN_SENDTO); if (err > 0) { return ESPCONN_IF; } return err; } else { pbuf_free(p); return ESPCONN_RTE; } }