Skip to content

Commit 35dee8f

Browse files
zhhyu7xiaoxiang781216
authored andcommitted
icmpv6: add SOCK_RAW type support
The third-party library we are porting will send and receive ICMPV6 messages (router_advert / router_solicit / neighbor_advert / neighbor_solicit etc.) from the user mode itself, so we added the SOCK_RAW related implementation for ICMPV6. Signed-off-by: zhanghongyu <[email protected]>
1 parent 6bf9891 commit 35dee8f

File tree

7 files changed

+405
-151
lines changed

7 files changed

+405
-151
lines changed

net/icmpv6/icmpv6.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <sys/types.h>
3131
#include <stdint.h>
3232
#include <assert.h>
33+
#include <netinet/icmp6.h>
3334

3435
#include <nuttx/mm/iob.h>
3536
#include <nuttx/net/ip.h>
@@ -84,7 +85,6 @@ struct icmpv6_conn_s
8485
/* ICMPv6-specific content follows */
8586

8687
uint16_t id; /* ICMPv6 ECHO request ID */
87-
uint8_t nreqs; /* Number of requests with no response received */
8888
uint8_t crefs; /* Reference counts on this instance */
8989

9090
/* The device that the ICMPv6 request was sent on */
@@ -97,13 +97,19 @@ struct icmpv6_conn_s
9797
*/
9898

9999
struct iob_queue_s readahead; /* Read-ahead buffering */
100+
struct icmp6_filter filter; /* ICMP6 type filter */
100101

101102
/* The following is a list of poll structures of threads waiting for
102103
* socket events.
103104
*/
104105

105106
struct icmpv6_poll_s pollinfo[CONFIG_NET_ICMPv6_NPOLLWAITERS];
106107
};
108+
109+
/* Callback from icmpv6_foreach() */
110+
111+
typedef int (*icmpv6_callback_t)(FAR struct icmpv6_conn_s *conn,
112+
FAR void *arg);
107113
#endif
108114

109115
#ifdef CONFIG_NET_ICMPv6_NEIGHBOR
@@ -596,20 +602,20 @@ FAR struct icmpv6_conn_s *icmpv6_nextconn(FAR struct icmpv6_conn_s *conn);
596602
#endif
597603

598604
/****************************************************************************
599-
* Name: icmpv6_findconn
605+
* Name: icmpv6_foreach
600606
*
601607
* Description:
602-
* Find an ICMPv6 connection structure that is expecting a ICMPv6 ECHO
603-
* response with this ID from this device
608+
* Enumerate each ICMPv6 connection structure. This function will terminate
609+
* when either (1) all connection have been enumerated or (2) when a
610+
* callback returns any non-zero value.
604611
*
605612
* Assumptions:
606613
* This function is called from network logic at with the network locked.
607614
*
608615
****************************************************************************/
609616

610617
#ifdef CONFIG_NET_ICMPv6_SOCKET
611-
FAR struct icmpv6_conn_s *icmpv6_findconn(FAR struct net_driver_s *dev,
612-
uint16_t id);
618+
int icmpv6_foreach(icmpv6_callback_t callback, FAR void *arg);
613619
#endif
614620

615621
/****************************************************************************

net/icmpv6/icmpv6_conn.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -252,31 +252,36 @@ FAR struct icmpv6_conn_s *icmpv6_nextconn(FAR struct icmpv6_conn_s *conn)
252252
}
253253

254254
/****************************************************************************
255-
* Name: icmpv6_findconn
255+
* Name: icmpv6_foreach
256256
*
257257
* Description:
258-
* Find an ICMPv6 connection structure that is expecting a ICMPv6 ECHO
259-
* response with this ID from this device
258+
* Enumerate each ICMPv6 connection structure. This function will terminate
259+
* when either (1) all connection have been enumerated or (2) when a
260+
* callback returns any non-zero value.
260261
*
261262
* Assumptions:
262263
* This function is called from network logic at with the network locked.
263264
*
264265
****************************************************************************/
265266

266-
FAR struct icmpv6_conn_s *icmpv6_findconn(FAR struct net_driver_s *dev,
267-
uint16_t id)
267+
int icmpv6_foreach(icmpv6_callback_t callback, FAR void *arg)
268268
{
269269
FAR struct icmpv6_conn_s *conn;
270+
int ret = 0;
270271

271-
for (conn = icmpv6_nextconn(NULL); conn != NULL;
272-
conn = icmpv6_nextconn(conn))
272+
if (callback != NULL)
273273
{
274-
if (conn->id == id && conn->dev == dev && conn->nreqs > 0)
274+
for (conn = icmpv6_nextconn(NULL); conn != NULL;
275+
conn = icmpv6_nextconn(conn))
275276
{
276-
return conn;
277+
ret = callback(conn, arg);
278+
if (ret != 0)
279+
{
280+
break;
281+
}
277282
}
278283
}
279284

280-
return conn;
285+
return ret;
281286
}
282287
#endif /* CONFIG_NET_ICMP */

net/icmpv6/icmpv6_input.c

Lines changed: 121 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,32 @@
5959
#define MLDREPORT_V2 ((FAR struct mld_mcast_listen_report_v2_s *)icmpv6)
6060
#define MLDDONE ((FAR struct mld_mcast_listen_done_s *)icmpv6)
6161

62+
#ifdef CONFIG_NET_ICMPv6_SOCKET
63+
64+
/****************************************************************************
65+
* Private Types
66+
****************************************************************************/
67+
68+
struct icmpv6_deliver_s
69+
{
70+
FAR struct net_driver_s *dev; /* Current network device */
71+
unsigned int iplen; /* The size of the IPv6 header */
72+
bool delivered; /* Whether the message is delivered */
73+
};
74+
6275
/****************************************************************************
6376
* Private Functions
6477
****************************************************************************/
6578

79+
static bool icmpv6_filter(FAR const uint32_t *data, uint8_t type)
80+
{
81+
/* We require only the four bytes of the ICMPv6 header. */
82+
83+
DEBUGASSERT(data != NULL);
84+
85+
return (data[type >> 5] & (1u << (type & 31))) != 0;
86+
}
87+
6688
/****************************************************************************
6789
* Name: icmpv6_datahandler
6890
*
@@ -82,7 +104,6 @@
82104
*
83105
****************************************************************************/
84106

85-
#ifdef CONFIG_NET_ICMPv6_SOCKET
86107
static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev,
87108
FAR struct icmpv6_conn_s *conn,
88109
unsigned int iplen)
@@ -93,9 +114,14 @@ static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev,
93114
uint16_t buflen;
94115
int ret;
95116

117+
iob = iob_tryalloc(false);
118+
if (iob == NULL)
119+
{
120+
return -ENOMEM;
121+
}
122+
96123
/* Put the IPv6 address at the beginning of the read-ahead buffer */
97124

98-
iob = dev->d_iob;
99125
ipv6 = IPv6BUF;
100126
inaddr.sin6_family = AF_INET6;
101127
inaddr.sin6_port = 0;
@@ -107,13 +133,19 @@ static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev,
107133

108134
memcpy(iob->io_data, &inaddr, sizeof(struct sockaddr_in6));
109135

110-
/* Copy the new ICMPv6 reply into the I/O buffer chain (without waiting) */
136+
iob_reserve(iob, sizeof(struct sockaddr_in6));
111137

112-
buflen = ICMPv6SIZE;
138+
/* Copy the ICMPv6 message into the I/O buffer chain (without waiting) */
113139

114-
/* Trim l3 header */
140+
ret = iob_clone_partial(dev->d_iob, dev->d_iob->io_pktlen,
141+
iplen, iob, 0, true, false);
142+
if (ret < 0)
143+
{
144+
iob_free_chain(iob);
145+
return ret;
146+
}
115147

116-
iob = iob_trimhead(iob, iplen);
148+
buflen = ICMPv6SIZE;
117149

118150
/* Add the new I/O buffer chain to the tail of the read-ahead queue (again
119151
* without waiting).
@@ -130,12 +162,81 @@ static uint16_t icmpv6_datahandler(FAR struct net_driver_s *dev,
130162
ninfo("Buffered %d bytes\n", buflen);
131163
}
132164

133-
/* Device buffer must be enqueue or freed, clear the handle */
134-
135-
netdev_iob_clear(dev);
136165
return buflen;
137166
}
138-
#endif
167+
168+
/****************************************************************************
169+
* Name: icmpv6_delivery_callback
170+
*
171+
* Description:
172+
* Copy the icmpv6 package to the application according to the filter
173+
* conditions, but ICMPv6_ECHO_REPLY is a special message type, if there
174+
* is an application waiting, it will also copy.
175+
*
176+
* Input Parameters:
177+
* conn - A pointer to the ICMPv6 connection structure.
178+
* arg - The context information
179+
*
180+
****************************************************************************/
181+
182+
static int icmpv6_delivery_callback(FAR struct icmpv6_conn_s *conn,
183+
FAR void *arg)
184+
{
185+
FAR struct icmpv6_deliver_s *info = arg;
186+
FAR struct net_driver_s *dev = info->dev;
187+
FAR struct icmpv6_hdr_s *icmpv6 = IPBUF(info->iplen);
188+
189+
if (icmpv6_filter(conn->filter.icmp6_filt, icmpv6->type) &&
190+
(icmpv6->type != ICMPv6_ECHO_REPLY || conn->id != ICMPv6REPLY->id ||
191+
conn->dev != dev))
192+
{
193+
return 0;
194+
}
195+
196+
info->delivered = true;
197+
if (devif_conn_event(dev, ICMPv6_NEWDATA, conn->sconn.list) !=
198+
ICMPv6_NEWDATA)
199+
{
200+
dev->d_len = dev->d_iob->io_pktlen;
201+
}
202+
else
203+
{
204+
icmpv6_datahandler(dev, conn, info->iplen);
205+
}
206+
207+
return 0;
208+
}
209+
210+
/****************************************************************************
211+
* Name: icmpv6_deliver
212+
*
213+
* Description:
214+
* Copy the icmpv6 package to the application according to the filter
215+
* conditions, but ICMPv6_ECHO_REPLY is a special message type, if there
216+
* is an application waiting, it will also copy.
217+
*
218+
* Input Parameters:
219+
* dev - Reference to a device driver structure.
220+
* iplen - The size of the IPv6 header. This may be larger than
221+
* IPv6_HDRLEN the IPv6 header if IPv6 extension headers are
222+
* present.
223+
*
224+
****************************************************************************/
225+
226+
static bool icmpv6_deliver(FAR struct net_driver_s *dev, unsigned int iplen)
227+
{
228+
struct icmpv6_deliver_s info;
229+
230+
info.dev = dev;
231+
info.iplen = iplen;
232+
info.delivered = false;
233+
234+
icmpv6_foreach(icmpv6_delivery_callback, &info);
235+
236+
return info.delivered;
237+
}
238+
239+
#endif /* CONFIG_NET_ICMPv6_SOCKET */
139240

140241
/****************************************************************************
141242
* Public Functions
@@ -166,6 +267,9 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen)
166267
{
167268
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
168269
FAR struct icmpv6_hdr_s *icmpv6 = IPBUF(iplen);
270+
#ifdef CONFIG_NET_ICMPv6_SOCKET
271+
bool delivered = icmpv6_deliver(dev, iplen);
272+
#endif
169273

170274
#ifdef CONFIG_NET_STATISTICS
171275
g_netstats.icmpv6.recv++;
@@ -443,61 +547,6 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen)
443547
}
444548
break;
445549

446-
#ifdef CONFIG_NET_ICMPv6_SOCKET
447-
/* If an ICMPv6 echo reply is received then there should also be
448-
* a thread waiting to received the echo response.
449-
*/
450-
451-
case ICMPv6_ECHO_REPLY:
452-
{
453-
FAR struct icmpv6_echo_reply_s *reply;
454-
FAR struct icmpv6_conn_s *conn;
455-
uint16_t flags = ICMPv6_NEWDATA;
456-
457-
/* Nothing consumed the ICMP reply. That might be because this is
458-
* an old, invalid reply or simply because the ping application
459-
* has not yet put its poll or recv in place.
460-
*/
461-
462-
/* Is there any connection that might expect this reply? */
463-
464-
reply = ICMPv6REPLY;
465-
conn = icmpv6_findconn(dev, reply->id);
466-
if (conn == NULL)
467-
{
468-
/* No.. drop the packet */
469-
470-
goto icmpv6_drop_packet;
471-
}
472-
473-
/* Dispatch the ECHO reply to the waiting thread */
474-
475-
flags = devif_conn_event(dev, flags, conn->sconn.list);
476-
477-
/* Was the ECHO reply consumed by any waiting thread? */
478-
479-
if ((flags & ICMPv6_NEWDATA) != 0)
480-
{
481-
uint16_t nbuffered;
482-
483-
/* Yes.. Add the ICMP echo reply to the IPPROTO_ICMP socket read
484-
* ahead buffer.
485-
*/
486-
487-
nbuffered = icmpv6_datahandler(dev, conn, iplen);
488-
if (nbuffered == 0)
489-
{
490-
/* Could not buffer the data.. drop the packet */
491-
492-
goto icmpv6_drop_packet;
493-
}
494-
}
495-
496-
goto icmpv6_send_nothing;
497-
}
498-
break;
499-
#endif
500-
501550
#ifdef CONFIG_NET_MLD
502551
/* Dispatch received Multicast Listener Discovery (MLD) packets. */
503552

@@ -556,6 +605,13 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned int iplen)
556605

557606
default:
558607
{
608+
#ifdef CONFIG_NET_ICMPv6_SOCKET
609+
if (delivered)
610+
{
611+
goto icmpv6_send_nothing;
612+
}
613+
#endif
614+
559615
nwarn("WARNING: Unknown ICMPv6 type: %d\n", icmpv6->type);
560616
goto icmpv6_type_error;
561617
}

0 commit comments

Comments
 (0)