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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * Implements the listener over Ethernet.
19 * Right now only supports non-VLAN frames Ethernet (not 802.3/802.2 LLC) frames.
28 #include <arpa/inet.h>
30 #include <net/ethernet.h>
32 #include <sys/types.h>
34 #include <sys/ioctl.h>
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>
41 #include <ccnx/forwarder/metis/core/metis_Dispatcher.h>
43 #include <ccnx/forwarder/metis/io/metis_GenericEther.h>
44 #include <ccnx/forwarder/metis/io/metis_EtherConnection.h>
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)
51 #include <LongBow/runtime.h>
52 #include <parc/algol/parc_Memory.h>
54 typedef struct metis_ether_stats {
57 uint64_t framesReceived;
58 uint64_t framesReassembled;
59 uint64_t framesNotForUs;
62 typedef struct metis_ether_listener {
63 MetisForwarder *metis;
66 MetisGenericEther *genericEther;
69 CPIAddress *localAddress;
74 PARCEvent *ether_event;
76 // what size do the read buffers need to be?
77 size_t ether_buffer_length;
79 // buffer to read next packet in to
80 PARCEventBuffer *nextReadBuffer;
82 // We store MAC addresses in uint64 and mask them down to 6 bytes.
83 // this means all our address comparisons are simple "==" operations
85 uint64_t *destinationAddressList;
86 size_t destinationAddressSize;
88 uint64_t *sourceAddressList;
89 size_t sourceAddressSize;
91 _MetisEtherStats stats;
92 } _MetisEtherListener;
94 // network byte order mask to go from 8-bytes to 6-bytes
95 #define MAC_MASK (htonll(0xFFFFFFFFFFFF0000ULL))
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);
103 static MetisListenerOps _etherTemplate = {
105 .destroy = &_metisEtherListener_OpsDestroy,
106 .getInterfaceIndex = &_metisEtherListener_OpsGetInterfaceIndex,
107 .getListenAddress = &_metisEtherListener_OpsGetListenAddress,
108 .getEncapType = &_metisEtherListener_OpsGetEncapType,
109 .getSocket = &_metisEtherListener_OpsGetSocket
112 // Called by Libevent
113 static void _metisEtherListener_ReadCallback(int fd, PARCEventType what, void *user_data);
116 _logStats(_MetisEtherListener *listener, PARCLogLevel level)
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,
122 listener->stats.framesIn,
123 listener->stats.framesError,
124 listener->stats.framesReceived,
125 listener->stats.framesReassembled,
126 listener->stats.framesNotForUs);
131 // =============================================
135 _metisEtherListener_FillInEthernetAddresses(_MetisEtherListener *etherListener)
138 PARCBuffer *myAddress = metisGenericEther_GetMacAddress(etherListener->genericEther);
139 uint64_t macAsUint64 = 0;
142 while (parcBuffer_Remaining(myAddress) > 0) {
143 uint8_t b = parcBuffer_GetUint8(myAddress);
147 // the mac address is only 6 bytes, so shift two more
149 parcBuffer_Rewind(myAddress);
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));
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
163 etherListener->sourceAddressList = parcMemory_AllocateAndClear(sizeof(uint64_t));
164 etherListener->sourceAddressSize = 1;
165 etherListener->sourceAddressList[0] = htonll(macAsUint64); // our address
169 _metisEtherListener_ReleaseEthernetAddresses(_MetisEtherListener *etherListener)
171 parcMemory_Deallocate((void **) ðerListener->destinationAddressList);
172 parcMemory_Deallocate((void **) ðerListener->sourceAddressList);
174 if (etherListener->localAddress) {
175 cpiAddress_Destroy(ðerListener->localAddress);
180 metisEtherListener_Create(MetisForwarder *metis, const char *deviceName, uint16_t ethertype)
182 assertNotNull(metis, "Parameter metis must be non-null");
183 assertNotNull(deviceName, "Parameter deviceName must be non-null");
185 _MetisEtherListener *etherListener = parcMemory_AllocateAndClear(sizeof(_MetisEtherListener));
186 etherListener->ethertype = ethertype;
187 etherListener->genericEther = metisGenericEther_Create(metis, deviceName, etherListener->ethertype);
189 MetisListenerOps *ops = NULL;
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);
197 int etherSocket = metisGenericEther_GetDescriptor(etherListener->genericEther);
198 bool persistent = true;
200 // now wrap it in an event callback
201 etherListener->ether_event = metisDispatcher_CreateNetworkEvent(
202 metisForwarder_GetDispatcher(etherListener->metis),
204 _metisEtherListener_ReadCallback,
208 assertNotNull(etherListener->ether_event, "got null event from metisDispatcher_CreateNetworkEvent: %s", strerror(errno));
210 // Setup the destination and source ethernet addresses we want to use
211 _metisEtherListener_FillInEthernetAddresses(etherListener);
213 // Finished all initialization, so start the network event.
214 metisDispatcher_StartNetworkEvent(metisForwarder_GetDispatcher(etherListener->metis), etherListener->ether_event);
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");
220 memcpy(ops, &_etherTemplate, sizeof(MetisListenerOps));
221 ops->context = etherListener;
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",
232 parcMemory_Deallocate((void **) &str);
235 // failed to setup an Ethernet device
236 parcMemory_Deallocate((void **) ðerListener);
243 metisEtherListener_GetGenericEtherFromListener(MetisListenerOps *listenerOps)
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");
248 _MetisEtherListener *etherListener = (_MetisEtherListener *) listenerOps->context;
249 return etherListener->genericEther;
253 _metisEtherListener_Destroy(_MetisEtherListener **listenerPtr)
255 assertNotNull(listenerPtr, "Parameter must be non-null double pointer");
256 assertNotNull(*listenerPtr, "Parameter must derefernce to non-null pointer");
258 _MetisEtherListener *etherListener = *listenerPtr;
259 parcEventBuffer_Destroy(&(etherListener->nextReadBuffer));
260 metisDispatcher_DestroyNetworkEvent(metisForwarder_GetDispatcher(etherListener->metis), ðerListener->ether_event);
262 metisLogger_Release(ðerListener->logger);
264 metisGenericEther_Release(ðerListener->genericEther);
265 _metisEtherListener_ReleaseEthernetAddresses(etherListener);
266 parcMemory_Deallocate((void **) ðerListener);
271 _metisEtherListener_OpsDestroy(MetisListenerOps **listenerOpsPtr)
273 MetisListenerOps *ops = *listenerOpsPtr;
274 _MetisEtherListener *etherListener = (_MetisEtherListener *) ops->context;
275 _metisEtherListener_Destroy(ðerListener);
276 parcMemory_Deallocate((void **) &ops);
277 *listenerOpsPtr = NULL;
281 _metisEtherListener_OpsGetInterfaceIndex(const MetisListenerOps *ops)
283 _MetisEtherListener *etherListener = (_MetisEtherListener *) ops->context;
284 return etherListener->id;
287 static const CPIAddress *
288 _metisEtherListener_OpsGetListenAddress(const MetisListenerOps *ops)
290 _MetisEtherListener *etherListener = (_MetisEtherListener *) ops->context;
291 return etherListener->localAddress;
294 static MetisEncapType
295 _metisEtherListener_OpsGetEncapType(const MetisListenerOps *ops)
297 return METIS_ENCAP_ETHER;
301 _metisEtherListener_OpsGetSocket(const MetisListenerOps *ops)
303 _MetisEtherListener *etherListener = (_MetisEtherListener *) ops->context;
304 return etherListener->ether_fd;
308 // =============================================
309 // Internal functions
312 * Construct an address pair to match the remote
314 * The pair will always be (ourMacAddress, header->sourceAddress), even if the
315 * packet was received via a group or broadcast dmac.
317 * @param [<#in out in,out#>] <#name#> <#description#>
319 * @retval <#value#> <#explanation#>
326 static MetisAddressPair *
327 _metisEtherListener_ConstructAddressPair(_MetisEtherListener *etherListener, PARCEventBuffer *buffer)
329 struct ether_header *header = (struct ether_header *) parcEventBuffer_Pullup(buffer, ETHER_HDR_LEN);
331 CPIAddress *remoteAddress = cpiAddress_CreateFromLink(header->ether_shost, ETHER_ADDR_LEN);
333 MetisAddressPair *pair = metisAddressPair_Create(etherListener->localAddress, remoteAddress);
334 cpiAddress_Destroy(&remoteAddress);
340 * Lookup a connection in the connection table based on an address pair
342 * <#Paragraphs Of Explanation#>
344 * @param [in] etherListener An allocated MetisEtherListener
345 * @param [in] pair The Address pair to lookup
347 * @return null Not found
348 * @return non-null The connection
357 static const MetisConnection *
358 _metisEtherListener_LookupConnectionId(_MetisEtherListener *etherListener, MetisAddressPair *pair)
360 MetisConnectionTable *connTable = metisForwarder_GetConnectionTable(etherListener->metis);
362 const MetisConnection *conn = metisConnectionTable_FindByAddressPair(connTable, pair);
367 * @function _metisEtherListener_CreateNewConnection
368 * @abstract Creates a new Metis connection for the peer
370 * PRECONDITION: you know there's not an existing connection with the address pair
372 * Creates a new connection and adds it to the connection table.
375 * @return The connection id for the new connection
377 static const MetisConnection *
378 _metisEtherListener_CreateNewConnection(_MetisEtherListener *etherListener, MetisAddressPair *pair)
380 // metisEtherConnection_Create takes ownership of the pair
381 MetisIoOperations *ops = metisEtherConnection_Create(etherListener->metis, etherListener->genericEther, pair);
382 MetisConnection *conn = metisConnection_Create(ops);
384 metisConnectionTable_Add(metisForwarder_GetConnectionTable(etherListener->metis), conn);
390 * Read an ethernet frame and return its buffer.
392 * Will use ether->nextReadBuffer. If we read a frame, will allocate a new nextReadBuffer.
394 * @param [in] fd The ethernet frame socket
396 * @return NULL could not read a frame
397 * @return non-null Ethernet frame in the buffer
404 static PARCEventBuffer *
405 _metisEtherListener_ReadEtherFrame(_MetisEtherListener *etherListener)
407 PARCEventBuffer *readBuffer = NULL;
409 bool success = metisGenericEther_ReadNextFrame(etherListener->genericEther, etherListener->nextReadBuffer);
412 readBuffer = etherListener->nextReadBuffer;
413 etherListener->nextReadBuffer = parcEventBuffer_Create();
415 if (metisLogger_IsLoggable(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
416 metisLogger_Log(etherListener->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
418 parcEventBuffer_GetLength(readBuffer));
426 * Compares source MAC address to our address
428 * buffer points to the start of the Ethernet frame. Checks if the source address
431 * The check is done against the array of addresses in ether->sourceAddressList
433 * @param [in] header The Ethernet header
435 * @return true The Ethernet source address is our MAC address
436 * @return false It is not our MAC address
444 _metisEtherListener_IsOurSourceAddress(_MetisEtherListener *ether, struct ether_header *header)
446 // this copies the source address, then masks our copy
448 memcpy(&u64_shost, header->ether_shost, ETHER_ADDR_LEN);
449 u64_shost &= MAC_MASK;
451 for (int i = 0; i < ether->sourceAddressSize; i++) {
452 if (u64_shost == ether->sourceAddressList[i]) {
460 * Compares destination MAC address to our receive addresses
462 * The check is done against the array of addresses in ether->destinationAddressList
464 * @param [<#in out in,out#>] <#name#> <#description#>
466 * @return <#value#> <#explanation#>
474 _metisEtherListener_IsOurDestinationAddress(_MetisEtherListener *ether, struct ether_header *header)
476 // this copies the source address, then masks our copy
478 memcpy(&u64_dhost, header->ether_dhost, ETHER_ADDR_LEN);
479 u64_dhost &= MAC_MASK;
481 for (int i = 0; i < ether->destinationAddressSize; i++) {
482 if (u64_dhost == ether->destinationAddressList[i]) {
490 * <#One Line Description#>
492 * <#Paragraphs Of Explanation#>
494 * @param [<#in out in,out#>] <#name#> <#description#>
496 * @return <#value#> <#explanation#>
504 _metisEtherListener_IsOurProtocol(_MetisEtherListener *ether, struct ether_header *header)
506 // TODO: check the ethertype
517 * Processes an ethernet frame to make sure its for us
519 * Ensures that the frame is for us, and not from our source address.
521 * @param [<#in out in,out#>] <#name#> <#description#>
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
533 _metisEtherListener_ParseEtherFrame(_MetisEtherListener *etherListener, PARCEventBuffer *buffer)
535 _ParseResult result = ParseResult_Error;
537 struct ether_header *header = (struct ether_header *) parcEventBuffer_Pullup(buffer, ETHER_HDR_LEN);
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.
547 result = ParseResult_Accept;
556 static const MetisConnection *
557 _metisEtherListener_LookupOrCreateConnection(_MetisEtherListener *etherListener, PARCEventBuffer *buffer)
559 MetisAddressPair *pair = _metisEtherListener_ConstructAddressPair(etherListener, buffer);
561 const MetisConnection *conn = _metisEtherListener_LookupConnectionId(etherListener, pair);
564 conn = _metisEtherListener_CreateNewConnection(etherListener, pair);
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);
573 metisAddressPair_Release(&pair);
579 * Accept a fragment, put it in rassembler, and pass reassembled frames up stack.
581 * precondition: message is not null
583 * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#>
585 * @return <#value#> <#explanation#>
595 _acceptFragment(_MetisEtherListener *etherListener, const MetisConnection *conn, MetisMessage *message)
597 MetisHopByHopFragmenter *fragmenter = metisEtherConnection_GetFragmenter(conn);
598 assertNotNull(fragmenter, "Could not get fragmenter from the underlying connection");
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);
611 _acceptFrame(_MetisEtherListener *etherListener, PARCEventBuffer *buffer, int fd)
613 const MetisConnection *conn = _metisEtherListener_LookupOrCreateConnection(etherListener, buffer);
615 // remove the ethernet header
616 parcEventBuffer_Read(buffer, NULL, ETHER_HDR_LEN);
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);
621 size_t readLength = parcEventBuffer_GetLength(buffer);
623 etherListener->stats.framesReceived++;
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",
630 metisConnection_GetConnectionId(conn));
631 _logStats(etherListener, PARCLogLevel_Debug);
634 _acceptFragment(etherListener, conn, message);
635 metisMessage_Release(&message);
638 etherListener->stats.framesError++;
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",
645 metisConnection_GetConnectionId(conn));
646 _logStats(etherListener, PARCLogLevel_Warning);
652 _rejectFrame(_MetisEtherListener *etherListener, PARCEventBuffer *buffer, int fd)
654 etherListener->stats.framesNotForUs++;
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),
661 _logStats(etherListener, PARCLogLevel_Warning);
666 _errorFrame(_MetisEtherListener *etherListener, PARCEventBuffer *buffer, int fd)
668 etherListener->stats.framesError++;
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),
675 _logStats(etherListener, PARCLogLevel_Warning);
680 _metisEtherListener_ReadCallback(int fd, PARCEventType what, void *user_data)
682 // ether is datagram based, we don't have a connection
683 _MetisEtherListener *etherListener = (_MetisEtherListener *) user_data;
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",
689 (what & PARCEventType_Timeout) ? " timeout" : "",
690 (what & PARCEventType_Read) ? " read" : "",
691 (what & PARCEventType_Write) ? " write" : "",
692 (what & PARCEventType_Signal) ? " signal" : "",
696 if (what & PARCEventType_Read) {
698 PARCEventBuffer *buffer = _metisEtherListener_ReadEtherFrame(etherListener);
700 if (buffer == NULL) {
704 etherListener->stats.framesIn++;
706 _ParseResult result = _metisEtherListener_ParseEtherFrame(etherListener, buffer);
709 case ParseResult_Accept:
710 _acceptFrame(etherListener, buffer, fd);
713 case ParseResult_Reject:
714 _rejectFrame(etherListener, buffer, fd);
715 parcEventBuffer_Destroy(&buffer);
718 case ParseResult_Error:
719 _errorFrame(etherListener, buffer, fd);
720 parcEventBuffer_Destroy(&buffer);
724 trapUnexpectedState("Do not understand parse result %d", result);