Initial commit: sb-forwarder, metis.
[cicn.git] / metis / ccnx / forwarder / metis / io / metis_EtherListener.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /**
17  * Implements the listener over Ethernet.
18  *
19  * Right now only supports non-VLAN frames Ethernet (not 802.3/802.2 LLC) frames.
20  *
21  */
22
23 #include <config.h>
24 #include <stdio.h>
25 #include <stdbool.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <arpa/inet.h>
29
30 #include <net/ethernet.h>
31
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <sys/ioctl.h>
35
36 #include <ccnx/forwarder/metis/core/metis_Forwarder.h>
37 #include <ccnx/forwarder/metis/core/metis_Connection.h>
38 #include <ccnx/forwarder/metis/core/metis_Message.h>
39 #include <ccnx/forwarder/metis/messenger/metis_Messenger.h>
40
41 #include <ccnx/forwarder/metis/core/metis_Dispatcher.h>
42
43 #include <ccnx/forwarder/metis/io/metis_GenericEther.h>
44 #include <ccnx/forwarder/metis/io/metis_EtherConnection.h>
45
46 #ifndef ntohll
47 #define ntohll(x) (((uint64_t) (ntohl((uint32_t) (x & 0x00000000FFFFFFFF))) << 32) | ntohl(((uint32_t) ((((uint64_t) x) >> 32)))))
48 #define htonll(x) ntohll(x)
49 #endif
50
51 #include <LongBow/runtime.h>
52 #include <parc/algol/parc_Memory.h>
53
54 typedef struct metis_ether_stats {
55     uint64_t framesIn;
56     uint64_t framesError;
57     uint64_t framesReceived;
58     uint64_t framesReassembled;
59     uint64_t framesNotForUs;
60 } _MetisEtherStats;
61
62 typedef struct metis_ether_listener {
63     MetisForwarder *metis;
64     MetisLogger *logger;
65
66     MetisGenericEther *genericEther;
67
68     unsigned id;
69     CPIAddress *localAddress;
70
71     uint16_t ethertype;
72
73     int ether_fd;
74     PARCEvent *ether_event;
75
76     // what size do the read buffers need to be?
77     size_t ether_buffer_length;
78
79     // buffer to read next packet in to
80     PARCEventBuffer *nextReadBuffer;
81
82     // We store MAC addresses in uint64 and mask them down to 6 bytes.
83     // this means all our address comparisons are simple "==" operations
84
85     uint64_t *destinationAddressList;
86     size_t destinationAddressSize;
87
88     uint64_t *sourceAddressList;
89     size_t sourceAddressSize;
90
91     _MetisEtherStats stats;
92 } _MetisEtherListener;
93
94 // network byte order mask to go from 8-bytes to 6-bytes
95 #define MAC_MASK          (htonll(0xFFFFFFFFFFFF0000ULL))
96
97 static void              _metisEtherListener_OpsDestroy(MetisListenerOps **listenerOpsPtr);
98 static unsigned          _metisEtherListener_OpsGetInterfaceIndex(const MetisListenerOps *ops);
99 static const CPIAddress *_metisEtherListener_OpsGetListenAddress(const MetisListenerOps *ops);
100 static MetisEncapType    _metisEtherListener_OpsGetEncapType(const MetisListenerOps *ops);
101 static int               _metisEtherListener_OpsGetSocket(const MetisListenerOps *ops);
102
103 static MetisListenerOps _etherTemplate = {
104     .context           = NULL,
105     .destroy           = &_metisEtherListener_OpsDestroy,
106     .getInterfaceIndex = &_metisEtherListener_OpsGetInterfaceIndex,
107     .getListenAddress  = &_metisEtherListener_OpsGetListenAddress,
108     .getEncapType      = &_metisEtherListener_OpsGetEncapType,
109     .getSocket         = &_metisEtherListener_OpsGetSocket
110 };
111
112 // Called by Libevent
113 static void _metisEtherListener_ReadCallback(int fd, PARCEventType what, void *user_data);
114
115 static void
116 _logStats(_MetisEtherListener *listener, PARCLogLevel level)
117 {
118     if (metisLogger_IsLoggable(listener->logger, MetisLoggerFacility_IO, level)) {
119         metisLogger_Log(listener->logger, MetisLoggerFacility_IO, level, __func__,
120                         "EtherListener %p frames in %" PRIu64 ", errors %" PRIu64 " ok %" PRIu64 " reassemble %" PRIu64 " reject %" PRIu64,
121                         (void *) listener,
122                         listener->stats.framesIn,
123                         listener->stats.framesError,
124                         listener->stats.framesReceived,
125                         listener->stats.framesReassembled,
126                         listener->stats.framesNotForUs);
127     }
128 }
129
130
131 // =============================================
132 // Public API
133
134 static void
135 _metisEtherListener_FillInEthernetAddresses(_MetisEtherListener *etherListener)
136 {
137     // this may be null
138     PARCBuffer *myAddress = metisGenericEther_GetMacAddress(etherListener->genericEther);
139     uint64_t macAsUint64 = 0;
140
141     if (myAddress) {
142         while (parcBuffer_Remaining(myAddress) > 0) {
143             uint8_t b = parcBuffer_GetUint8(myAddress);
144             macAsUint64 <<= 8;
145             macAsUint64 |= b;
146         }
147         // the mac address is only 6 bytes, so shift two more
148         macAsUint64 <<= 16;
149         parcBuffer_Rewind(myAddress);
150
151         // loopback interface as a 0-length link address
152         if (parcBuffer_Remaining(myAddress) > 0) {
153             etherListener->localAddress = cpiAddress_CreateFromLink(parcBuffer_Overlay(myAddress, 0), parcBuffer_Remaining(myAddress));
154         }
155     }
156
157     etherListener->destinationAddressList = parcMemory_AllocateAndClear(sizeof(uint64_t) * 3);
158     etherListener->destinationAddressSize = 3;
159     etherListener->destinationAddressList[0] = htonll(macAsUint64);         // our address
160     etherListener->destinationAddressList[1] = htonll(0x01005E0017AA0000);  // CCN address
161     etherListener->destinationAddressList[2] = htonll(0xFFFFFFFFFFFF0000);  // broadcast
162
163     etherListener->sourceAddressList = parcMemory_AllocateAndClear(sizeof(uint64_t));
164     etherListener->sourceAddressSize = 1;
165     etherListener->sourceAddressList[0] = htonll(macAsUint64);         // our address
166 }
167
168 static void
169 _metisEtherListener_ReleaseEthernetAddresses(_MetisEtherListener *etherListener)
170 {
171     parcMemory_Deallocate((void **) &etherListener->destinationAddressList);
172     parcMemory_Deallocate((void **) &etherListener->sourceAddressList);
173
174     if (etherListener->localAddress) {
175         cpiAddress_Destroy(&etherListener->localAddress);
176     }
177 }
178
179 MetisListenerOps *
180 metisEtherListener_Create(MetisForwarder *metis, const char *deviceName, uint16_t ethertype)
181 {
182     assertNotNull(metis, "Parameter metis must be non-null");
183     assertNotNull(deviceName, "Parameter deviceName must be non-null");
184
185     _MetisEtherListener *etherListener = parcMemory_AllocateAndClear(sizeof(_MetisEtherListener));
186     etherListener->ethertype = ethertype;
187     etherListener->genericEther = metisGenericEther_Create(metis, deviceName, etherListener->ethertype);
188
189     MetisListenerOps *ops = NULL;
190
191     if (etherListener->genericEther != NULL) {
192         etherListener->metis = metis;
193         etherListener->logger = metisLogger_Acquire(metisForwarder_GetLogger(metis));
194         etherListener->nextReadBuffer = parcEventBuffer_Create();
195         etherListener->id = metisForwarder_GetNextConnectionId(metis);
196
197         int etherSocket = metisGenericEther_GetDescriptor(etherListener->genericEther);
198         bool persistent = true;
199
200         // now wrap it in an event callback
201         etherListener->ether_event = metisDispatcher_CreateNetworkEvent(
202             metisForwarder_GetDispatcher(etherListener->metis),
203             persistent,
204             _metisEtherListener_ReadCallback,
205             etherListener,
206             etherSocket);
207
208         assertNotNull(etherListener->ether_event, "got null event from metisDispatcher_CreateNetworkEvent: %s", strerror(errno));
209
210         // Setup the destination and source ethernet addresses we want to use
211         _metisEtherListener_FillInEthernetAddresses(etherListener);
212
213         // Finished all initialization, so start the network event.
214         metisDispatcher_StartNetworkEvent(metisForwarder_GetDispatcher(etherListener->metis), etherListener->ether_event);
215
216         // Construct an instance of the MetisListenerOps particular to this context.
217         ops = parcMemory_Allocate(sizeof(MetisListenerOps));
218         assertNotNull(ops, "Got null from parc_memory_new");
219
220         memcpy(ops, &_etherTemplate, sizeof(MetisListenerOps));
221         ops->context = etherListener;
222
223         if (metisLogger_IsLoggable(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
224             char *str = cpiAddress_ToString(etherListener->localAddress);
225             metisLogger_Log(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
226                             "Create Ethernet Listener id %d on %s addr %s ethertype 0x%04x ether socket %d",
227                             etherListener->id,
228                             deviceName,
229                             str,
230                             ethertype,
231                             etherSocket);
232             parcMemory_Deallocate((void **) &str);
233         }
234     } else {
235         // failed to setup an Ethernet device
236         parcMemory_Deallocate((void **) &etherListener);
237     }
238
239     return ops;
240 }
241
242 MetisGenericEther *
243 metisEtherListener_GetGenericEtherFromListener(MetisListenerOps *listenerOps)
244 {
245     assertNotNull(listenerOps, "Parameter listenerOps must be non-null");
246     assertTrue(listenerOps->getEncapType(listenerOps) == METIS_ENCAP_ETHER, "Can only call on a METIS_ENCAP_ETHER listener");
247
248     _MetisEtherListener *etherListener = (_MetisEtherListener *) listenerOps->context;
249     return etherListener->genericEther;
250 }
251
252 static void
253 _metisEtherListener_Destroy(_MetisEtherListener **listenerPtr)
254 {
255     assertNotNull(listenerPtr, "Parameter must be non-null double pointer");
256     assertNotNull(*listenerPtr, "Parameter must derefernce to non-null pointer");
257
258     _MetisEtherListener *etherListener = *listenerPtr;
259     parcEventBuffer_Destroy(&(etherListener->nextReadBuffer));
260     metisDispatcher_DestroyNetworkEvent(metisForwarder_GetDispatcher(etherListener->metis), &etherListener->ether_event);
261
262     metisLogger_Release(&etherListener->logger);
263
264     metisGenericEther_Release(&etherListener->genericEther);
265     _metisEtherListener_ReleaseEthernetAddresses(etherListener);
266     parcMemory_Deallocate((void **) &etherListener);
267     *listenerPtr = NULL;
268 }
269
270 static void
271 _metisEtherListener_OpsDestroy(MetisListenerOps **listenerOpsPtr)
272 {
273     MetisListenerOps *ops = *listenerOpsPtr;
274     _MetisEtherListener *etherListener = (_MetisEtherListener *) ops->context;
275     _metisEtherListener_Destroy(&etherListener);
276     parcMemory_Deallocate((void **) &ops);
277     *listenerOpsPtr = NULL;
278 }
279
280 static unsigned
281 _metisEtherListener_OpsGetInterfaceIndex(const MetisListenerOps *ops)
282 {
283     _MetisEtherListener *etherListener = (_MetisEtherListener *) ops->context;
284     return etherListener->id;
285 }
286
287 static const CPIAddress *
288 _metisEtherListener_OpsGetListenAddress(const MetisListenerOps *ops)
289 {
290     _MetisEtherListener *etherListener = (_MetisEtherListener *) ops->context;
291     return etherListener->localAddress;
292 }
293
294 static MetisEncapType
295 _metisEtherListener_OpsGetEncapType(const MetisListenerOps *ops)
296 {
297     return METIS_ENCAP_ETHER;
298 }
299
300 static int
301 _metisEtherListener_OpsGetSocket(const MetisListenerOps *ops)
302 {
303     _MetisEtherListener *etherListener = (_MetisEtherListener *) ops->context;
304     return etherListener->ether_fd;
305 }
306
307
308 // =============================================
309 // Internal functions
310
311 /**
312  * Construct an address pair to match the remote
313  *
314  * The pair will always be (ourMacAddress, header->sourceAddress), even if the
315  * packet was received via a group or broadcast dmac.
316  *
317  * @param [<#in out in,out#>] <#name#> <#description#>
318  *
319  * @retval <#value#> <#explanation#>
320  *
321  * Example:
322  * @code
323  * <#example#>
324  * @endcode
325  */
326 static MetisAddressPair *
327 _metisEtherListener_ConstructAddressPair(_MetisEtherListener *etherListener, PARCEventBuffer *buffer)
328 {
329     struct ether_header *header = (struct ether_header *) parcEventBuffer_Pullup(buffer, ETHER_HDR_LEN);
330
331     CPIAddress *remoteAddress = cpiAddress_CreateFromLink(header->ether_shost, ETHER_ADDR_LEN);
332
333     MetisAddressPair *pair = metisAddressPair_Create(etherListener->localAddress, remoteAddress);
334     cpiAddress_Destroy(&remoteAddress);
335
336     return pair;
337 }
338
339 /**
340  * Lookup a connection in the connection table based on an address pair
341  *
342  * <#Paragraphs Of Explanation#>
343  *
344  * @param [in] etherListener An allocated MetisEtherListener
345  * @param [in] pair The Address pair to lookup
346  *
347  * @return null Not found
348  * @return non-null The connection
349  *
350  * Example:
351  * @code
352  * {
353  *     <#example#>
354  * }
355  * @endcode
356  */
357 static const MetisConnection *
358 _metisEtherListener_LookupConnectionId(_MetisEtherListener *etherListener, MetisAddressPair *pair)
359 {
360     MetisConnectionTable *connTable = metisForwarder_GetConnectionTable(etherListener->metis);
361
362     const MetisConnection *conn = metisConnectionTable_FindByAddressPair(connTable, pair);
363     return conn;
364 }
365
366 /**
367  * @function _metisEtherListener_CreateNewConnection
368  * @abstract Creates a new Metis connection for the peer
369  * @discussion
370  *   PRECONDITION: you know there's not an existing connection with the address pair
371  *
372  *   Creates a new connection and adds it to the connection table.
373  *
374  * @param <#param1#>
375  * @return The connection id for the new connection
376  */
377 static const MetisConnection *
378 _metisEtherListener_CreateNewConnection(_MetisEtherListener *etherListener, MetisAddressPair *pair)
379 {
380     // metisEtherConnection_Create takes ownership of the pair
381     MetisIoOperations *ops = metisEtherConnection_Create(etherListener->metis, etherListener->genericEther, pair);
382     MetisConnection *conn = metisConnection_Create(ops);
383
384     metisConnectionTable_Add(metisForwarder_GetConnectionTable(etherListener->metis), conn);
385
386     return conn;
387 }
388
389 /**
390  * Read an ethernet frame and return its buffer.
391  *
392  * Will use ether->nextReadBuffer.  If we read a frame, will allocate a new nextReadBuffer.
393  *
394  * @param [in] fd The ethernet frame socket
395  *
396  * @return NULL could not read a frame
397  * @return non-null Ethernet frame in the buffer
398  *
399  * Example:
400  * @code
401  * <#example#>
402  * @endcode
403  */
404 static PARCEventBuffer *
405 _metisEtherListener_ReadEtherFrame(_MetisEtherListener *etherListener)
406 {
407     PARCEventBuffer *readBuffer = NULL;
408
409     bool success = metisGenericEther_ReadNextFrame(etherListener->genericEther, etherListener->nextReadBuffer);
410
411     if (success) {
412         readBuffer = etherListener->nextReadBuffer;
413         etherListener->nextReadBuffer = parcEventBuffer_Create();
414
415         if (metisLogger_IsLoggable(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
416             metisLogger_Log(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
417                             "read %zu bytes",
418                             parcEventBuffer_GetLength(readBuffer));
419         }
420     }
421
422     return readBuffer;
423 }
424
425 /**
426  * Compares source MAC address to our address
427  *
428  * buffer points to the start of the Ethernet frame.  Checks if the source address
429  * is our address.
430  *
431  * The check is done against the array of addresses in ether->sourceAddressList
432  *
433  * @param [in] header The Ethernet header
434  *
435  * @return true  The Ethernet source address is our MAC address
436  * @return false It is not our MAC address
437  *
438  * Example:
439  * @code
440  * <#example#>
441  * @endcode
442  */
443 static bool
444 _metisEtherListener_IsOurSourceAddress(_MetisEtherListener *ether, struct ether_header *header)
445 {
446     // this copies the source address, then masks our copy
447     uint64_t u64_shost;
448     memcpy(&u64_shost, header->ether_shost, ETHER_ADDR_LEN);
449     u64_shost &= MAC_MASK;
450
451     for (int i = 0; i < ether->sourceAddressSize; i++) {
452         if (u64_shost == ether->sourceAddressList[i]) {
453             return true;
454         }
455     }
456     return false;
457 }
458
459 /**
460  * Compares destination MAC address to our receive addresses
461  *
462  * The check is done against the array of addresses in ether->destinationAddressList
463  *
464  * @param [<#in out in,out#>] <#name#> <#description#>
465  *
466  * @return <#value#> <#explanation#>
467  *
468  * Example:
469  * @code
470  * <#example#>
471  * @endcode
472  */
473 static bool
474 _metisEtherListener_IsOurDestinationAddress(_MetisEtherListener *ether, struct ether_header *header)
475 {
476     // this copies the source address, then masks our copy
477     uint64_t u64_dhost;
478     memcpy(&u64_dhost, header->ether_dhost, ETHER_ADDR_LEN);
479     u64_dhost &= MAC_MASK;
480
481     for (int i = 0; i < ether->destinationAddressSize; i++) {
482         if (u64_dhost == ether->destinationAddressList[i]) {
483             return true;
484         }
485     }
486     return false;
487 }
488
489 /**
490  * <#One Line Description#>
491  *
492  * <#Paragraphs Of Explanation#>
493  *
494  * @param [<#in out in,out#>] <#name#> <#description#>
495  *
496  * @return <#value#> <#explanation#>
497  *
498  * Example:
499  * @code
500  * <#example#>
501  * @endcode
502  */
503 static bool
504 _metisEtherListener_IsOurProtocol(_MetisEtherListener *ether, struct ether_header *header)
505 {
506     // TODO: check the ethertype
507     return true;
508 }
509
510 typedef enum {
511     ParseResult_Accept,
512     ParseResult_Reject,
513     ParseResult_Error
514 } _ParseResult;
515
516 /**
517  * Processes an ethernet frame to make sure its for us
518  *
519  * Ensures that the frame is for us, and not from our source address.
520  *
521  * @param [<#in out in,out#>] <#name#> <#description#>
522  *
523  * @return ParseResult_Accept We should recieve and process the frame
524  * @return ParseResult_Reject Do not receive the frame
525  * @return ParseResult_Error There was an error looking at the Ethernet header
526  *
527  * Example:
528  * @code
529  * <#example#>
530  * @endcode
531  */
532 static _ParseResult
533 _metisEtherListener_ParseEtherFrame(_MetisEtherListener *etherListener, PARCEventBuffer *buffer)
534 {
535     _ParseResult result = ParseResult_Error;
536
537     struct ether_header *header = (struct ether_header *) parcEventBuffer_Pullup(buffer, ETHER_HDR_LEN);
538
539     if (header) {
540         result = ParseResult_Reject;
541         if (_metisEtherListener_IsOurProtocol(etherListener, header)) {
542             if (_metisEtherListener_IsOurDestinationAddress(etherListener, header)) {
543                 if (!_metisEtherListener_IsOurSourceAddress(etherListener, header)) {
544                     // ok, it is the right protocol, a good destination address
545                     // and not our source address.  We should ccept this.
546
547                     result = ParseResult_Accept;
548                 }
549             }
550         }
551     }
552
553     return result;
554 }
555
556 static const MetisConnection *
557 _metisEtherListener_LookupOrCreateConnection(_MetisEtherListener *etherListener, PARCEventBuffer *buffer)
558 {
559     MetisAddressPair *pair = _metisEtherListener_ConstructAddressPair(etherListener, buffer);
560
561     const MetisConnection *conn = _metisEtherListener_LookupConnectionId(etherListener, pair);
562
563     if (!conn) {
564         conn = _metisEtherListener_CreateNewConnection(etherListener, pair);
565
566         if (metisLogger_IsLoggable(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
567             char *str = metisAddressPair_ToString(pair);
568             metisLogger_Log(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
569                             "Create connid %u address pair %s", metisConnection_GetConnectionId(conn), str);
570             free(str);
571         }
572     }
573     metisAddressPair_Release(&pair);
574
575     return conn;
576 }
577
578 /**
579  * Accept a fragment, put it in rassembler, and pass reassembled frames up stack.
580  *
581  * precondition: message is not null
582  *
583  * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#>
584  *
585  * @return <#value#> <#explanation#>
586  *
587  * Example:
588  * @code
589  * {
590  *     <#example#>
591  * }
592  * @endcode
593  */
594 static void
595 _acceptFragment(_MetisEtherListener *etherListener, const MetisConnection *conn, MetisMessage *message)
596 {
597     MetisHopByHopFragmenter *fragmenter = metisEtherConnection_GetFragmenter(conn);
598     assertNotNull(fragmenter, "Could not get fragmenter from the underlying connection");
599
600     bool receiveQueueNotEmpty = metisHopByHopFragmenter_Receive(fragmenter, message);
601     if (receiveQueueNotEmpty) {
602         MetisMessage *assembled = NULL;
603         while ((assembled = metisHopByHopFragmenter_PopReceiveQueue(fragmenter)) != NULL) {
604             etherListener->stats.framesReassembled++;
605             metisForwarder_Receive(etherListener->metis, assembled);
606         }
607     }
608 }
609
610 static void
611 _acceptFrame(_MetisEtherListener *etherListener, PARCEventBuffer *buffer, int fd)
612 {
613     const MetisConnection *conn = _metisEtherListener_LookupOrCreateConnection(etherListener, buffer);
614
615     // remove the ethernet header
616     parcEventBuffer_Read(buffer, NULL, ETHER_HDR_LEN);
617
618     // takes ownership of buffer (will destroy it if there's an error)
619     MetisMessage *message = metisMessage_CreateFromBuffer(metisConnection_GetConnectionId(conn), metisForwarder_GetTicks(etherListener->metis), buffer, etherListener->logger);
620
621     size_t readLength = parcEventBuffer_GetLength(buffer);
622     if (message) {
623         etherListener->stats.framesReceived++;
624
625         if (metisLogger_IsLoggable(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
626             metisLogger_Log(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
627                             "read %zu bytes from fd %d connid %d",
628                             readLength,
629                             fd,
630                             metisConnection_GetConnectionId(conn));
631             _logStats(etherListener, PARCLogLevel_Debug);
632         }
633
634         _acceptFragment(etherListener, conn, message);
635         metisMessage_Release(&message);
636
637     } else {
638         etherListener->stats.framesError++;
639
640         if (metisLogger_IsLoggable(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning)) {
641             metisLogger_Log(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__,
642                             "read %zu bytes from fd %d connid %d: Error parsing skeleton",
643                             readLength,
644                             fd,
645                             metisConnection_GetConnectionId(conn));
646             _logStats(etherListener, PARCLogLevel_Warning);
647         }
648     }
649 }
650
651 static void
652 _rejectFrame(_MetisEtherListener *etherListener, PARCEventBuffer *buffer, int fd)
653 {
654     etherListener->stats.framesNotForUs++;
655
656     if (metisLogger_IsLoggable(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning)) {
657         metisLogger_Log(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__,
658                         "read %zu bytes from fd %d: reject frame",
659                         parcEventBuffer_GetLength(buffer),
660                         fd);
661         _logStats(etherListener, PARCLogLevel_Warning);
662     }
663 }
664
665 static void
666 _errorFrame(_MetisEtherListener *etherListener, PARCEventBuffer *buffer, int fd)
667 {
668     etherListener->stats.framesError++;
669
670     if (metisLogger_IsLoggable(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning)) {
671         metisLogger_Log(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__,
672                         "read %zu bytes from fd %d: error parsing Ethernet header",
673                         parcEventBuffer_GetLength(buffer),
674                         fd);
675         _logStats(etherListener, PARCLogLevel_Warning);
676     }
677 }
678
679 static void
680 _metisEtherListener_ReadCallback(int fd, PARCEventType what, void *user_data)
681 {
682     // ether is datagram based, we don't have a connection
683     _MetisEtherListener *etherListener = (_MetisEtherListener *) user_data;
684
685     if (metisLogger_IsLoggable(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
686         metisLogger_Log(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
687                         "socket %d what %s%s%s%s data %p",
688                         fd,
689                         (what & PARCEventType_Timeout) ? " timeout" : "",
690                         (what & PARCEventType_Read)    ? " read" : "",
691                         (what & PARCEventType_Write)   ? " write" : "",
692                         (what & PARCEventType_Signal)  ? " signal" : "",
693                         user_data);
694     }
695
696     if (what & PARCEventType_Read) {
697         while (true) {
698             PARCEventBuffer *buffer = _metisEtherListener_ReadEtherFrame(etherListener);
699
700             if (buffer == NULL) {
701                 break;
702             }
703
704             etherListener->stats.framesIn++;
705
706             _ParseResult result = _metisEtherListener_ParseEtherFrame(etherListener, buffer);
707
708             switch (result) {
709                 case ParseResult_Accept:
710                     _acceptFrame(etherListener, buffer, fd);
711                     break;
712
713                 case ParseResult_Reject:
714                     _rejectFrame(etherListener, buffer, fd);
715                     parcEventBuffer_Destroy(&buffer);
716                     break;
717
718                 case ParseResult_Error:
719                     _errorFrame(etherListener, buffer, fd);
720                     parcEventBuffer_Destroy(&buffer);
721                     break;
722
723                 default:
724                     trapUnexpectedState("Do not understand parse result %d", result);
725             }
726         }
727     }
728 }