Coverage Report

Created: 2026-09-27 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/arch/posix/eventloop_posix_eth.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
 *
5
 *   Copyright 2018 (c) Kontron Europe GmbH (Author: Rudolf Hoyler)
6
 *   Copyright 2019-2020 (c) Kalycito Infotech Private Limited
7
 *   Copyright 2019-2020 (c) Wind River Systems, Inc.
8
 *   Copyright 2022 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
9
 *   Copyright 2025 (c) SICK AG (Author: Joerg Fischer)
10
 */
11
12
#include "eventloop_posix.h"
13
14
#if defined(UA_ARCHITECTURE_POSIX) && !defined(UA_ARCHITECTURE_LWIP) && defined(__linux__)
15
16
#include <arpa/inet.h> /* htons */
17
#include <net/ethernet.h> /* ETH_P_*/
18
#include <linux/if_packet.h>
19
#include <linux/net_tstamp.h> /* txtime */
20
21
/* Configuration parameters */
22
23
20.6k
#define ETH_MANAGERPARAMS 2
24
25
static UA_KeyValueRestriction ethManagerParams[ETH_MANAGERPARAMS] = {
26
    {{0, UA_STRING_STATIC("recv-bufsize")}, &UA_TYPES[UA_TYPES_UINT32], false, true, false},
27
    {{0, UA_STRING_STATIC("send-bufsize")}, &UA_TYPES[UA_TYPES_UINT32], false, true, false}
28
};
29
30
0
#define ETH_PARAMETERSSIZE 15
31
0
#define ETH_PARAMINDEX_ADDR 0
32
0
#define ETH_PARAMINDEX_LISTEN 1
33
0
#define ETH_PARAMINDEX_IFACE 2
34
0
#define ETH_PARAMINDEX_ETHERTYPE 3
35
0
#define ETH_PARAMINDEX_VID 4
36
0
#define ETH_PARAMINDEX_PCP 5
37
0
#define ETH_PARAMINDEX_DEI 6
38
0
#define ETH_PARAMINDEX_PROMISCUOUS 7
39
0
#define ETH_PARAMINDEX_PRIORITY 8
40
0
#define ETH_PARAMINDEX_TXTIME_ENABLE 9
41
0
#define ETH_PARAMINDEX_TXTIME_FLAGS 10
42
0
#define ETH_PARAMINDEX_TXTIME 11
43
0
#define ETH_PARAMINDEX_TXTIME_PICO 12
44
0
#define ETH_PARAMINDEX_TXTIME_DROP 13
45
0
#define ETH_PARAMINDEX_VALIDATE 14
46
47
static UA_KeyValueRestriction ethConnectionParams[ETH_PARAMETERSSIZE+1] = {
48
    {{0, UA_STRING_STATIC("address")}, &UA_TYPES[UA_TYPES_STRING], false, true, false},
49
    {{0, UA_STRING_STATIC("listen")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false},
50
    {{0, UA_STRING_STATIC("interface")}, &UA_TYPES[UA_TYPES_STRING], true, true, false},
51
    {{0, UA_STRING_STATIC("ethertype")}, &UA_TYPES[UA_TYPES_UINT16], false, true, false},
52
    {{0, UA_STRING_STATIC("vid")}, &UA_TYPES[UA_TYPES_UINT16], false, true, false},
53
    {{0, UA_STRING_STATIC("pcp")}, &UA_TYPES[UA_TYPES_BYTE], false, true, false},
54
    {{0, UA_STRING_STATIC("dei")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false},
55
    {{0, UA_STRING_STATIC("promiscuous")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false},
56
    {{0, UA_STRING_STATIC("priority")}, &UA_TYPES[UA_TYPES_UINT32], false, true, false},
57
    {{0, UA_STRING_STATIC("txtime-enable")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false},
58
    {{0, UA_STRING_STATIC("txtime-flags")}, &UA_TYPES[UA_TYPES_UINT32], false, true, false},
59
    {{0, UA_STRING_STATIC("txtime")}, &UA_TYPES[UA_TYPES_DATETIME], false, true, false},
60
    {{0, UA_STRING_STATIC("txtime-pico")}, &UA_TYPES[UA_TYPES_UINT16], false, true, false},
61
    {{0, UA_STRING_STATIC("txtime-drop-late")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false},
62
    {{0, UA_STRING_STATIC("validate")}, &UA_TYPES[UA_TYPES_BOOLEAN], false, true, false},
63
    /* Duplicated address parameter with a scalar value required. For the send-socket case. */
64
    {{0, UA_STRING_STATIC("address")}, &UA_TYPES[UA_TYPES_STRING], true, true, false},
65
};
66
67
0
#define UA_ETH_MAXHEADERLENGTH (2*ETHER_ADDR_LEN)+4+2+2
68
69
typedef struct {
70
    UA_RegisteredFD rfd;
71
72
    UA_ConnectionManager_connectionCallback applicationCB;
73
    void *application;
74
    void *context;
75
76
    struct sockaddr_ll sll;
77
    /* The Ethernet header to prepend for sending frames is precomputed and reused.
78
     * The length field (the last 2 byte) is adjusted.
79
     * - 2 * ETHER_ADDR_LEN: destination and source
80
     * - 4 byte: VLAN tagging (optional)
81
     * - 2 byte: EtherType (optional)
82
     * - 2 byte: length */
83
    unsigned char header[UA_ETH_MAXHEADERLENGTH];
84
    unsigned char headerSize;
85
    unsigned char lengthOffset; /* No length field if zero */
86
87
    UA_Boolean txtimeEnabled;
88
} ETH_FD;
89
90
/* The format of a Ethernet address is six groups of hexadecimal digits,
91
 * separated by hyphens (e.g. 01-23-45-67-89-ab). */
92
static UA_StatusCode
93
0
parseEthAddress(const UA_String *buf, UA_Byte *addr) {
94
0
    size_t curr = 0, idx = 0;
95
0
    for(; idx < ETHER_ADDR_LEN; idx++) {
96
0
        UA_UInt32 value;
97
0
        size_t progress = UA_readNumberWithBase(&buf->data[curr],
98
0
                                                buf->length - curr, &value, 16);
99
0
        if(progress == 0 || value > (long)0xff)
100
0
            return UA_STATUSCODE_BADINTERNALERROR;
101
102
0
        addr[idx] = (UA_Byte) value;
103
104
0
        curr += progress;
105
0
        if(curr == buf->length)
106
0
            break;
107
108
0
        if(buf->data[curr] != '-')
109
0
            return UA_STATUSCODE_BADINTERNALERROR;
110
111
0
        curr++; /* skip '-' */
112
0
    }
113
114
0
    if(idx != (ETH_ALEN-1))
115
0
        return UA_STATUSCODE_BADINTERNALERROR;
116
117
0
    return UA_STATUSCODE_GOOD;
118
0
}
119
120
static UA_Boolean
121
0
isMulticastEthAddress(const UA_Byte *address) {
122
0
    if((address[0] & 1) == 0)
123
0
        return false; /* Unicast address */
124
0
    for(size_t i = 0; i < ETHER_ADDR_LEN; i++) {
125
0
        if(address[i] != 0xff)
126
0
            return true; /* Not broadcast address ff-ff-ff-ff-ff-ff */
127
0
    }
128
0
    return false;
129
0
}
130
131
static void
132
0
setAddrString(unsigned char addrStr[18], unsigned char addr[ETHER_ADDR_LEN]) {
133
0
    mp_snprintf((char*)addrStr, 18, "%02x-%02x-%02x-%02x-%02x-%02x",
134
0
                addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
135
0
}
136
137
/* Return zero if parsing failed */
138
static size_t
139
parseETHHeader(const UA_ByteString *buf,
140
               unsigned char destAddr[ETHER_ADDR_LEN],
141
               unsigned char sourceAddr[ETHER_ADDR_LEN],
142
               UA_UInt16 *etherType, UA_UInt16 *vid,
143
0
               UA_Byte *pcp, UA_Boolean *dei) {
144
0
    if(buf->length < (2 * ETHER_ADDR_LEN)+2)
145
0
        return 0;
146
147
    /* Parse "normal" Ethernet header */
148
0
    memcpy(destAddr, buf->data, ETHER_ADDR_LEN);
149
0
    memcpy(sourceAddr, &buf->data[ETHER_ADDR_LEN], ETHER_ADDR_LEN);
150
0
    size_t pos = 2 * ETHER_ADDR_LEN;
151
0
    UA_UInt16 length = ntohs(*(UA_UInt16*)&buf->data[pos]);
152
0
    pos += 2;
153
154
    /* No EtherType and no VLAN */
155
0
    if(length <= 1500)
156
0
        return pos;
157
158
    /* Parse 802.1Q VLAN header */
159
0
    if(length == 0x8100) {
160
0
        if(buf->length < (2 * ETHER_ADDR_LEN)+2+4)
161
0
            return 0;
162
0
        pos += 2;
163
0
        UA_UInt16 tci = ntohs(*(UA_UInt16*)&buf->data[pos]);
164
0
        *pcp = (tci >> 13) & 0x07;
165
0
        *dei = (tci >> 12) & 0x01;
166
0
        *vid = tci & 0x0FFF;
167
0
        pos += 2;
168
0
        length = ntohs(*(UA_UInt16*)&buf->data[pos]);
169
0
    }
170
171
    /* Set the EtherType if it is set */
172
0
    if(length > 1500)
173
0
        *etherType = length;
174
175
0
    return pos;
176
0
}
177
178
static unsigned char
179
setETHHeader(unsigned char *buf,
180
             unsigned char destAddr[ETHER_ADDR_LEN],
181
             unsigned char sourceAddr[ETHER_ADDR_LEN],
182
             UA_UInt16 etherType, UA_UInt16 vid,
183
0
             UA_Byte pcp, UA_Boolean dei, unsigned char *lengthOffset) {
184
    /* Set dest and source address */
185
0
    size_t pos = 0;
186
0
    memcpy(buf, destAddr, ETHER_ADDR_LEN);
187
0
    pos += ETHER_ADDR_LEN;
188
0
    memcpy(&buf[pos], sourceAddr, ETHER_ADDR_LEN);
189
0
    pos += ETHER_ADDR_LEN;
190
191
    /* Set the 802.1Q VLAN header for VIDs in the valid range 1-4094. */
192
0
    if(vid > 0 && vid <= 4094) {
193
0
        *(UA_UInt16*)&buf[pos] = htons(0x8100);
194
0
        pos += 2;
195
0
        UA_UInt16 tci = (UA_UInt16)(((UA_UInt16)pcp  << 13) | ((UA_UInt16)dei  << 12) | ((UA_UInt16)vid));
196
0
        *(UA_UInt16*)&buf[pos] = htons(tci);
197
0
        pos += 2;
198
0
    }
199
200
    /* Set the Ethertype or store the offset for the length field */
201
0
    if(etherType == 0 || etherType == ETH_P_ALL) {
202
0
        *lengthOffset = (unsigned char)pos;
203
0
    } else {
204
0
        *(UA_UInt16*)&buf[pos] = htons(etherType);
205
0
    }
206
0
    pos += 2;
207
0
    return (unsigned char)pos;
208
0
}
209
210
static UA_StatusCode
211
ETH_allocNetworkBuffer(UA_ConnectionManager *cm, uintptr_t connectionId,
212
0
                       UA_ByteString *buf, size_t bufSize) {
213
    /* Get the ETH_FD */
214
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
215
0
    UA_FD fd = (UA_FD)connectionId;
216
0
    ETH_FD *erfd = (ETH_FD*)ZIP_FIND(UA_FDTree, &pcm->fds, &fd);
217
0
    if(!erfd)
218
0
        return UA_STATUSCODE_BADCONNECTIONREJECTED;
219
220
    /* Allocate the buffer with the hidden Ethernet header in front.
221
     * Spec 7.3.3: "MaxNetworkMessageSize shall be limited to an Ethernet
222
     * frame size of 1522 Byte." Reject oversized frames. */
223
0
    if(bufSize > (size_t)1522 - erfd->headerSize) {
224
0
        return UA_STATUSCODE_BADENCODINGERROR;
225
0
    }
226
    /* Reserve a fixed prefix. This lets the buffer be recovered even if the
227
     * connection disappears between allocation and send. */
228
0
    UA_StatusCode res =
229
0
        UA_EventLoopPOSIX_allocNetworkBuffer(cm, connectionId, buf,
230
0
                                             bufSize + UA_ETH_MAXHEADERLENGTH);
231
0
    if(UA_LIKELY(res == UA_STATUSCODE_GOOD)) {
232
0
        buf->data   += UA_ETH_MAXHEADERLENGTH;
233
0
        buf->length -= UA_ETH_MAXHEADERLENGTH;
234
0
    }
235
0
    return res;
236
0
}
237
238
static void
239
ETH_freeNetworkBuffer(UA_ConnectionManager *cm, uintptr_t connectionId,
240
0
                      UA_ByteString *buf) {
241
    /* Allocation uses a fixed prefix, so freeing does not depend on the
242
     * connection still being registered. */
243
0
    buf->data   -= UA_ETH_MAXHEADERLENGTH;
244
0
    buf->length += UA_ETH_MAXHEADERLENGTH;
245
0
    UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf);
246
0
}
247
248
static void
249
ETH_freeSendBuffer(UA_ConnectionManager *cm, uintptr_t connectionId,
250
0
                   UA_ByteString *buf, size_t headerSize) {
251
    /* Restore the application payload view expected by
252
     * ETH_freeNetworkBuffer after sendWithConnection exposed the header. */
253
0
    buf->data += headerSize;
254
0
    buf->length -= headerSize;
255
0
    ETH_freeNetworkBuffer(cm, connectionId, buf);
256
0
}
257
258
/* Test if the ConnectionManager can be stopped */
259
static void
260
20.6k
ETH_checkStopped(UA_POSIXConnectionManager *pcm) {
261
20.6k
    UA_LOCK_ASSERT(&((UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop)->elMutex);
262
263
20.6k
    if(pcm->fdsSize == 0 &&
264
20.6k
       pcm->cm.eventSource.state == UA_EVENTSOURCESTATE_STOPPING) {
265
20.6k
        UA_LOG_DEBUG(pcm->cm.eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK,
266
20.6k
                     "ETH\t| All sockets closed, the EventLoop has stopped");
267
20.6k
        pcm->cm.eventSource.state = UA_EVENTSOURCESTATE_STOPPED;
268
20.6k
    }
269
20.6k
}
270
271
/* This method must not be called from the application directly, but from within
272
 * the EventLoop. Otherwise we cannot be sure whether the file descriptor is
273
 * still used after calling close. */
274
static void
275
0
ETH_close(UA_POSIXConnectionManager *pcm, ETH_FD *conn) {
276
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
277
0
    UA_LOCK_ASSERT(&el->elMutex);
278
279
0
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
280
0
                 "ETH %u\t| Closing connection",
281
0
                 (unsigned)conn->rfd.fd);
282
283
    /* Deregister from the EventLoop */
284
0
    UA_EventLoopPOSIX_deregisterFD(el, &conn->rfd);
285
286
    /* Deregister internally */
287
0
    ZIP_REMOVE(UA_FDTree, &pcm->fds, &conn->rfd);
288
0
    UA_assert(pcm->fdsSize > 0);
289
0
    pcm->fdsSize--;
290
291
    /* Signal closing to the application */
292
0
    conn->applicationCB(&pcm->cm, (uintptr_t)conn->rfd.fd,
293
0
                        conn->application, &conn->context,
294
0
                        UA_CONNECTIONSTATE_CLOSING,
295
0
                        &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
296
297
    /* Close the socket */
298
0
    UA_RESET_ERRNO;
299
0
    int ret = UA_close(conn->rfd.fd);
300
0
    if(ret == 0) {
301
0
        UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
302
0
                    "ETH %u\t| Socket closed", (unsigned)conn->rfd.fd);
303
0
    } else {
304
0
        UA_LOG_SOCKET_ERRNO_WRAP(
305
0
           UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
306
0
                          "ETH %u\t| Could not close the socket (%s)",
307
0
                          (unsigned)conn->rfd.fd, errno_str));
308
0
    }
309
310
    /* Don't call free here. This might be done automatically via the delayed
311
     * callback that calls ETH_close. */
312
    /* UA_free(rfd); */
313
314
    /* Stop if the ucm is stopping and this was the last open socket */
315
0
    ETH_checkStopped(pcm);
316
0
}
317
318
static void
319
0
ETH_delayedClose(void *application, void *context) {
320
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)application;
321
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
322
0
    ETH_FD *conn = (ETH_FD*)context;
323
0
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
324
0
                 "ETH %u\t| Delayed closing of the connection",
325
0
                 (unsigned)conn->rfd.fd);
326
0
    UA_LOCK(&el->elMutex);
327
0
    ETH_close(pcm, conn);
328
0
    UA_UNLOCK(&el->elMutex);
329
0
    UA_free(conn);
330
0
}
331
332
/* Gets called when a socket receives data or closes */
333
static void
334
ETH_connectionSocketCallback(UA_ConnectionManager *cm, UA_RegisteredFD *rfd,
335
0
                             short event) {
336
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
337
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
338
0
    UA_LOCK_ASSERT(&el->elMutex);
339
340
0
    ETH_FD *conn = (ETH_FD*)rfd;
341
0
    if(event == UA_FDEVENT_ERR) {
342
0
        UA_LOG_SOCKET_ERRNO_WRAP(
343
0
           UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
344
0
                        "ETH %u\t| recv signaled the socket was shutdown (%s)",
345
0
                        (unsigned)rfd->fd, errno_str));
346
0
        ETH_close(pcm, conn);
347
0
        UA_free(rfd);
348
0
        return;
349
0
    }
350
351
    /* Use the already allocated receive-buffer */
352
0
    UA_ByteString response = pcm->rxBuffer;
353
354
    /* Receive */
355
0
    UA_RESET_ERRNO;
356
0
#ifndef UA_ARCHITECTURE_WIN32
357
0
    ssize_t ret = UA_recv(rfd->fd, (char*)response.data,
358
0
                          response.length, MSG_DONTWAIT);
359
#else
360
    int ret = UA_recv(rfd->fd, (char*)response.data,
361
                      response.length, MSG_DONTWAIT);
362
#endif
363
364
    /* Receive has failed */
365
0
    if(ret <= 0) {
366
0
        if(ret < 0 && UA_ERRNO == UA_INTERRUPTED)
367
0
            return;
368
369
        /* Orderly shutdown of the socket. We can immediately close as no method
370
         * "below" in the call stack will use the socket in this iteration of
371
         * the EventLoop. */
372
0
        UA_LOG_SOCKET_ERRNO_WRAP(
373
0
           UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
374
0
                        "ETH %u\t| recv signaled the socket was shutdown (%s)",
375
0
                        (unsigned)rfd->fd, errno_str));
376
0
        ETH_close(pcm, conn);
377
0
        UA_free(rfd);
378
0
        return;
379
0
    }
380
381
0
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
382
0
                 "ETH %u\t| Received message of size %u",
383
0
                 (unsigned)rfd->fd, (unsigned)ret);
384
385
0
    response.length = (size_t)ret;
386
387
    /* Parse the Ethernet header */
388
0
    unsigned char destAddr[ETHER_ADDR_LEN];
389
0
    unsigned char sourceAddr[ETHER_ADDR_LEN];
390
0
    UA_UInt16 etherType = 0;
391
0
    UA_UInt16 vid = 0;
392
0
    UA_Byte pcp = 0;
393
0
    UA_Boolean dei = 0;
394
0
    size_t headerSize = parseETHHeader(&response, destAddr, sourceAddr,
395
0
                                       &etherType, &vid, &pcp, &dei);
396
0
    if(headerSize == 0)
397
0
        return;
398
399
    /* Set up the parameter arguments passed to the application */
400
0
    unsigned char destAddrBytes[18];
401
0
    unsigned char sourceAddrBytes[18];
402
0
    setAddrString(destAddrBytes, destAddr);
403
0
    setAddrString(sourceAddrBytes, sourceAddr);
404
0
    UA_String destAddrStr = {17, destAddrBytes};
405
0
    UA_String sourceAddrStr = {17, sourceAddrBytes};
406
407
0
    size_t paramsSize = 2;
408
0
    UA_KeyValuePair params[6];
409
0
    params[0].key = UA_QUALIFIEDNAME(0, "destination-address");
410
0
    UA_Variant_setScalar(&params[0].value, &destAddrStr, &UA_TYPES[UA_TYPES_STRING]);
411
0
    params[1].key = UA_QUALIFIEDNAME(0, "source-address");
412
0
    UA_Variant_setScalar(&params[1].value, &sourceAddrStr, &UA_TYPES[UA_TYPES_STRING]);
413
414
0
    if(etherType > 0) {
415
0
        params[2].key = UA_QUALIFIEDNAME(0, "ethertype");
416
0
        UA_Variant_setScalar(&params[2].value, &etherType, &UA_TYPES[UA_TYPES_UINT16]);
417
0
        paramsSize++;
418
0
    }
419
420
0
    if(vid > 0) {
421
0
        params[paramsSize].key = UA_QUALIFIEDNAME(0, "vid");
422
0
        UA_Variant_setScalar(&params[paramsSize].value, &vid, &UA_TYPES[UA_TYPES_UINT16]);
423
0
        params[paramsSize+1].key = UA_QUALIFIEDNAME(0, "pcp");
424
0
        UA_Variant_setScalar(&params[paramsSize+1].value, &pcp, &UA_TYPES[UA_TYPES_BYTE]);
425
0
        params[paramsSize+2].key = UA_QUALIFIEDNAME(0, "dei");
426
0
        UA_Variant_setScalar(&params[paramsSize+2].value, &dei, &UA_TYPES[UA_TYPES_BOOLEAN]);
427
0
        paramsSize += 3;
428
0
    }
429
430
    /* Callback to the application layer with the Ethernet header hidden */
431
0
    UA_KeyValueMap map = {paramsSize, params};
432
0
    response.data += headerSize;
433
0
    response.length -= headerSize;
434
0
    conn->applicationCB(cm, (uintptr_t)rfd->fd, conn->application, &conn->context,
435
0
                        UA_CONNECTIONSTATE_ESTABLISHED, &map, response);
436
0
    response.data -= headerSize;
437
0
    response.length += headerSize;
438
0
}
439
440
static UA_StatusCode
441
ETH_openListenConnection(UA_EventLoopPOSIX *el, ETH_FD *conn,
442
                         const UA_KeyValueMap *params,
443
                         int ifindex, UA_UInt16 etherType,
444
0
                         UA_Boolean validate) {
445
0
    UA_LOCK_ASSERT(&el->elMutex);
446
447
    /* Bind the socket to interface and EtherType. Don't receive anything else. */
448
0
    struct sockaddr_ll sll;
449
0
    memset(&sll, 0, sizeof(struct sockaddr_ll));
450
0
    sll.sll_family = AF_PACKET;
451
0
    sll.sll_protocol = htons(etherType);
452
0
    sll.sll_ifindex = ifindex;
453
0
    if(!validate && bind(conn->rfd.fd, (struct sockaddr*)&sll, sizeof(sll)) < 0)
454
0
        return UA_STATUSCODE_BADINTERNALERROR;
455
456
    /* Immediately register for listen events. Don't have to wait for a
457
     * connection to open. */
458
0
    conn->rfd.listenEvents = UA_FDEVENT_IN;
459
460
    /* Set receiving to promiscuous (all target host addresses) */
461
0
    UA_RESET_ERRNO;
462
0
    const UA_Boolean *promiscuous = (const UA_Boolean*)
463
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_PROMISCUOUS].name,
464
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
465
0
    if(promiscuous && *promiscuous) {
466
0
        struct packet_mreq mreq;
467
0
        memset(&mreq, 0, sizeof(struct packet_mreq));
468
0
        mreq.mr_ifindex = ifindex;
469
0
        mreq.mr_type = PACKET_MR_PROMISC;
470
0
        int ret = UA_setsockopt(conn->rfd.fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
471
0
                             &mreq, sizeof(mreq));
472
0
        if(ret < 0) {
473
0
            UA_LOG_SOCKET_ERRNO_WRAP(
474
0
               UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
475
0
                            "ETH %u\t| Could not set raw socket to promiscuous mode %s",
476
0
                            (unsigned)conn->rfd.fd, errno_str));
477
0
            return UA_STATUSCODE_BADINTERNALERROR;
478
0
        } else {
479
0
            UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
480
0
                        "ETH %u\t| The socket was set to promiscuous mode",
481
0
                        (unsigned)conn->rfd.fd);
482
0
        }
483
0
    }
484
485
    /* Register for multicast if an address is defined */
486
0
    const UA_String *address = (const UA_String*)
487
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_ADDR].name,
488
0
                                 &UA_TYPES[UA_TYPES_STRING]);
489
0
    if(address) {
490
0
        UA_Byte addr[ETHER_ADDR_LEN];
491
0
        UA_StatusCode res = parseEthAddress(address, addr);
492
0
        if(res != UA_STATUSCODE_GOOD) {
493
0
            UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
494
0
                         "ETH\t| Address for listening cannot be parsed");
495
0
            return res;
496
0
        }
497
498
0
        if(!isMulticastEthAddress(addr)) {
499
0
            UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
500
0
                         "ETH\t| Address for listening is not a multicast address. Ignoring.");
501
0
            return UA_STATUSCODE_GOOD;
502
0
        }
503
504
0
        UA_RESET_ERRNO;
505
0
        struct packet_mreq mreq;
506
0
        memset(&mreq, 0, sizeof(struct packet_mreq));
507
0
        mreq.mr_ifindex = ifindex;
508
0
        mreq.mr_type = PACKET_MR_MULTICAST;
509
0
        mreq.mr_alen = ETH_ALEN;
510
0
        memcpy(mreq.mr_address, addr, ETHER_ADDR_LEN);
511
0
        if(!validate && UA_setsockopt(conn->rfd.fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
512
0
                                      (char *)&mreq, sizeof(mreq)) < 0) {
513
0
            UA_LOG_SOCKET_ERRNO_WRAP(
514
0
               UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
515
0
                            "ETH\t| Registering for multicast failed with error %s",
516
0
                            errno_str));
517
0
            return UA_STATUSCODE_BADINTERNALERROR;
518
0
        }
519
0
    }
520
521
0
    UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
522
0
                "ETH %u\t| Opened an Ethernet listen socket",
523
0
                (unsigned)conn->rfd.fd);
524
525
0
    return UA_STATUSCODE_GOOD;
526
0
}
527
528
static UA_StatusCode
529
ETH_openSendConnection(UA_EventLoopPOSIX *el, ETH_FD *conn, const UA_KeyValueMap *params,
530
0
                       UA_Byte source[ETHER_ADDR_LEN], int ifindex, UA_UInt16 etherType) {
531
0
    UA_LOCK_ASSERT(&el->elMutex);
532
533
    /* Parse the target address (has to exist) */
534
0
    const UA_String *address = (const UA_String*)
535
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_ADDR].name,
536
0
                                 &UA_TYPES[UA_TYPES_STRING]);
537
0
    UA_Byte dest[ETHER_ADDR_LEN];
538
0
    UA_StatusCode res = parseEthAddress(address, dest);
539
0
    if(res != UA_STATUSCODE_GOOD) {
540
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
541
0
                     "ETH\t| Could not parse the Ethernet address \"%.*s\"",
542
0
                     (int)address->length, (char*)address->data);
543
0
        return res;
544
0
    }
545
546
    /* Get the VLAN config */
547
0
    UA_UInt16 vid = 0;
548
0
    UA_Byte pcp = 0;
549
0
    UA_Boolean eid = false;
550
551
0
    const UA_UInt16 *vidp = (const UA_UInt16*)
552
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_VID].name,
553
0
                                 &UA_TYPES[UA_TYPES_UINT16]);
554
0
    if(vidp)
555
0
        vid = *vidp;
556
557
0
    const UA_Byte *pcpp = (const UA_Byte*)
558
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_PCP].name,
559
0
                                 &UA_TYPES[UA_TYPES_BYTE]);
560
0
    if(pcpp)
561
0
        pcp = *pcpp;
562
563
0
    const UA_Boolean *eidp = (const UA_Boolean*)
564
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_DEI].name,
565
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
566
0
    if(eidp)
567
0
        eid = *eidp;
568
569
    /* Store the structure for sendto */
570
0
    conn->sll.sll_ifindex = ifindex;
571
0
    conn->sll.sll_halen = ETH_ALEN;
572
0
    memcpy(conn->sll.sll_addr, dest, ETHER_ADDR_LEN);
573
574
    /* Generate the Ethernet header */
575
0
    conn->headerSize = setETHHeader(conn->header, dest, source, etherType,
576
0
                                    vid, pcp, eid, &conn->lengthOffset);
577
578
    /* Set the send priority if defined */
579
0
    const UA_Int32 *soPriority = (const UA_Int32*)
580
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_PRIORITY].name,
581
0
                                 &UA_TYPES[UA_TYPES_INT32]);
582
0
    if(soPriority) {
583
0
        UA_RESET_ERRNO;
584
0
        int prioRes = UA_setsockopt(conn->rfd.fd, SOL_SOCKET, SO_PRIORITY,
585
0
                                 soPriority, sizeof(int));
586
0
        if(prioRes != 0) {
587
0
            UA_LOG_SOCKET_ERRNO_WRAP(
588
0
               UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
589
0
                            "UA_setsockopt SO_PRIORITY failed with error %s", errno_str));
590
0
            return UA_STATUSCODE_BADINTERNALERROR;
591
0
        }
592
0
    }
593
594
    /* Enable txtime sending */
595
0
    const UA_Boolean *txtime_enable = (const UA_Boolean*)
596
0
        UA_KeyValueMap_getScalar(params,
597
0
                                 ethConnectionParams[ETH_PARAMINDEX_TXTIME_ENABLE].name,
598
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
599
600
0
    if(txtime_enable && *txtime_enable) {
601
#ifndef SO_TXTIME
602
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
603
                       "ETH %u\t| txtime feature not supported",
604
                       (unsigned)conn->rfd.fd);
605
#else
606
0
        const UA_UInt32 *txtime_flags = (const UA_UInt32*)
607
0
        UA_KeyValueMap_getScalar(params,
608
0
                                 ethConnectionParams[ETH_PARAMINDEX_TXTIME_FLAGS].name,
609
0
                                 &UA_TYPES[UA_TYPES_UINT32]);
610
0
        UA_RESET_ERRNO;
611
0
        struct sock_txtime so_txtime_val;
612
0
        memset(&so_txtime_val, 0, sizeof(struct sock_txtime));
613
0
        so_txtime_val.clockid = el->clockSourceMonotonic;
614
0
        so_txtime_val.flags = SOF_TXTIME_REPORT_ERRORS;
615
0
        if(txtime_flags)
616
0
            so_txtime_val.flags = *txtime_flags;
617
0
        if(UA_setsockopt(conn->rfd.fd, SOL_SOCKET, SO_TXTIME,
618
0
                      &so_txtime_val, sizeof(so_txtime_val)) == 0) {
619
0
            conn->txtimeEnabled = true;
620
0
        } else {
621
0
            UA_LOG_SOCKET_ERRNO_WRAP(
622
0
               UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
623
0
                              "ETH %u\t| Could not enable txtime (%s)",
624
0
                              (unsigned)conn->rfd.fd, errno_str));
625
0
        }
626
0
#endif
627
0
    }
628
629
    /* Done creating the socket */
630
0
    UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
631
0
                "ETH %u\t| Opened an Ethernet send socket",
632
0
                (unsigned)conn->rfd.fd);
633
634
0
    return UA_STATUSCODE_GOOD;
635
0
}
636
637
static UA_StatusCode
638
ETH_openConnection(UA_ConnectionManager *cm, const UA_KeyValueMap *params,
639
                   void *application, void *context,
640
0
                   UA_ConnectionManager_connectionCallback connectionCallback) {
641
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
642
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)cm->eventSource.eventLoop;
643
644
0
    UA_LOCK(&el->elMutex);
645
646
    /* Listen or send connection? */
647
0
    const UA_Boolean *listen = (const UA_Boolean*)
648
0
        UA_KeyValueMap_getScalar(params,
649
0
                                 ethConnectionParams[ETH_PARAMINDEX_LISTEN].name,
650
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
651
0
    size_t ethParamRestrictions = ETH_PARAMETERSSIZE;
652
0
    if(!listen || !*listen)
653
0
        ethParamRestrictions++; /* Use the last restriction only for send connections */
654
655
    /* Validate the parameters */
656
0
    UA_StatusCode res =
657
0
        UA_KeyValueRestriction_validate(el->eventLoop.logger, "ETH", ethConnectionParams,
658
0
                                        ethParamRestrictions, params);
659
0
    if(res != UA_STATUSCODE_GOOD) {
660
0
        UA_UNLOCK(&el->elMutex);
661
0
        return res;
662
0
    }
663
664
    /* Only validate the parameters? */
665
0
    UA_Boolean validate = false;
666
0
    const UA_Boolean *validateParam = (const UA_Boolean*)
667
0
        UA_KeyValueMap_getScalar(params,
668
0
                                 ethConnectionParams[ETH_PARAMINDEX_VALIDATE].name,
669
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
670
0
    if(validateParam)
671
0
        validate = *validateParam;
672
673
    /* Get the EtherType parameter */
674
0
    UA_UInt16 etherType = ETH_P_ALL;
675
0
    const UA_UInt16 *etParam =  (const UA_UInt16*)
676
0
        UA_KeyValueMap_getScalar(params,
677
0
                                 ethConnectionParams[ETH_PARAMINDEX_ETHERTYPE].name,
678
0
                                 &UA_TYPES[UA_TYPES_UINT16]);
679
0
    if(etParam)
680
0
        etherType = *etParam;
681
682
    /* Get the interface index */
683
0
    const UA_String *interface = (const UA_String*)
684
0
        UA_KeyValueMap_getScalar(params,
685
0
                                 ethConnectionParams[ETH_PARAMINDEX_IFACE].name,
686
0
                                 &UA_TYPES[UA_TYPES_STRING]);
687
0
    if (interface == NULL) {
688
0
        UA_UNLOCK(&el->elMutex);
689
0
        return UA_STATUSCODE_BADINTERNALERROR;
690
0
    }
691
0
    if(interface->length >= 128) {
692
0
        UA_UNLOCK(&el->elMutex);
693
0
        return UA_STATUSCODE_BADINTERNALERROR;
694
0
    }
695
0
    char ifname[128];
696
0
    if(interface->length)
697
0
        memcpy(ifname, interface->data, interface->length);
698
0
    ifname[interface->length] = 0;
699
0
    int ifindex = (int)if_nametoindex(ifname);
700
0
    if(ifindex == 0) {
701
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
702
0
                     "ETH\t| Could not find the interface %s", ifname);
703
0
        UA_UNLOCK(&el->elMutex);
704
0
        return UA_STATUSCODE_BADINTERNALERROR;
705
0
    }
706
707
    /* Create the socket and add the basic configuration */
708
0
    ETH_FD *conn = NULL;
709
0
    UA_FD sockfd;
710
0
    if(listen && *listen)
711
0
        sockfd = UA_socket(PF_PACKET, SOCK_RAW, htons(etherType));
712
0
    else
713
0
        sockfd = UA_socket(PF_PACKET, SOCK_RAW, 0); /* Don't receive */
714
0
    if(sockfd == -1) {
715
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
716
0
                     "ETH\t| Could not create a raw Ethernet socket (are you root?)");
717
0
        UA_UNLOCK(&el->elMutex);
718
0
        return UA_STATUSCODE_BADINTERNALERROR;
719
0
    }
720
721
    /* SO_REUSEADDR is unnecessary (and unsupported on some Linux systems):
722
     * res |= UA_EventLoopPOSIX_setReusable(sockfd); */
723
0
    res |= UA_EventLoopPOSIX_setNonBlocking(sockfd);
724
0
    res |= UA_EventLoopPOSIX_setNoSigPipe(sockfd);
725
0
    if(res != UA_STATUSCODE_GOOD)
726
0
        goto cleanup;
727
728
    /* Create the FD object */
729
0
    conn = (ETH_FD*)UA_calloc(1, sizeof(ETH_FD));
730
0
    if(!conn) {
731
0
        res = UA_STATUSCODE_BADOUTOFMEMORY;
732
0
        goto cleanup;
733
0
    }
734
735
0
    conn->rfd.fd = sockfd;
736
0
    conn->rfd.es = &pcm->cm.eventSource;
737
0
    conn->rfd.eventSourceCB = (UA_FDCallback)ETH_connectionSocketCallback;
738
0
    conn->context = context;
739
0
    conn->application = application;
740
0
    conn->applicationCB = connectionCallback;
741
742
    /* Configure a listen or a send connection */
743
0
    if(!listen || !*listen) {
744
        /* Get the source address for the interface */
745
0
        UA_RESET_ERRNO;
746
0
        struct ifreq ifr;
747
0
        memcpy(ifr.ifr_name, ifname, interface->length);
748
0
        ifr.ifr_name[interface->length] = 0;
749
0
        int result = ioctl(conn->rfd.fd, SIOCGIFHWADDR, &ifr);
750
0
        if(result == -1) {
751
0
            UA_LOG_SOCKET_ERRNO_WRAP(
752
0
               UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
753
0
                            "ETH %u\t| Cannot get the source address, %s",
754
0
                            (unsigned)conn->rfd.fd, errno_str));
755
0
            res = UA_STATUSCODE_BADCONNECTIONREJECTED;
756
0
            goto cleanup;
757
0
        }
758
0
        res = ETH_openSendConnection(el, conn, params,
759
0
                                     (unsigned char*)ifr.ifr_hwaddr.sa_data,
760
0
                                     ifindex, etherType);
761
0
    } else {
762
0
        res = ETH_openListenConnection(el, conn, params, ifindex, etherType, validate);
763
0
    }
764
765
    /* Don't actually open or shut down */
766
0
    if(validate || res != UA_STATUSCODE_GOOD)
767
0
        goto cleanup;
768
769
    /* Register in the EventLoop */
770
0
    res = UA_EventLoopPOSIX_registerFD(el, &conn->rfd);
771
0
    if(res != UA_STATUSCODE_GOOD)
772
0
        goto cleanup;
773
774
    /* Register locally */
775
0
    ZIP_INSERT(UA_FDTree, &pcm->fds, &conn->rfd);
776
0
    pcm->fdsSize++;
777
778
    /* Register the listen socket in the application */
779
0
    connectionCallback(cm, (uintptr_t)sockfd, application, &conn->context,
780
0
                       UA_CONNECTIONSTATE_ESTABLISHED, &UA_KEYVALUEMAP_NULL,
781
0
                       UA_BYTESTRING_NULL);
782
0
    UA_UNLOCK(&el->elMutex);
783
0
    return UA_STATUSCODE_GOOD;
784
785
0
 cleanup:
786
0
    UA_close(sockfd);
787
0
    UA_free(conn);
788
0
    UA_UNLOCK(&el->elMutex);
789
0
    return res;
790
0
}
791
792
static void
793
0
ETH_shutdown(UA_POSIXConnectionManager *pcm, ETH_FD *conn) {
794
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
795
0
    UA_LOCK_ASSERT(&((UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop)->elMutex);
796
797
0
    UA_DelayedCallback *dc = &conn->rfd.dc;
798
0
    if(dc->callback) {
799
0
        UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
800
0
                    "ETH %u\t| Cannot close - already closing",
801
0
                    (unsigned)conn->rfd.fd);
802
0
        return;
803
0
    }
804
805
    /* Shutdown the socket to cancel the current select/epoll */
806
0
    UA_shutdown(conn->rfd.fd, UA_SHUT_RDWR);
807
808
0
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
809
0
                 "ETH %u\t| Shutdown called", (unsigned)conn->rfd.fd);
810
811
0
    dc->callback = ETH_delayedClose;
812
0
    dc->application = pcm;
813
0
    dc->context = conn;
814
815
    /* Adding a delayed callback does not take a lock */
816
0
    UA_EventLoopPOSIX_addDelayedCallback((UA_EventLoop*)el, dc);
817
0
}
818
819
static UA_StatusCode
820
0
ETH_shutdownConnection(UA_ConnectionManager *cm, uintptr_t connectionId) {
821
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
822
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
823
0
    UA_LOCK(&el->elMutex);
824
825
    /* Get the ETH_FD */
826
0
    UA_FD fd = (UA_FD)connectionId;
827
0
    UA_RegisteredFD *rfd = ZIP_FIND(UA_FDTree, &pcm->fds, &fd);
828
0
    if(!rfd) {
829
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
830
0
                       "ETH\t| Cannot close Ethernet connection %u - not found",
831
0
                       (unsigned)connectionId);
832
0
        UA_UNLOCK(&el->elMutex);
833
0
        return UA_STATUSCODE_BADNOTFOUND;
834
0
    }
835
836
0
    ETH_shutdown(pcm, (ETH_FD*)rfd);
837
0
    UA_UNLOCK(&el->elMutex);
838
0
    return UA_STATUSCODE_GOOD;
839
0
}
840
841
#ifdef SO_TXTIME
842
static ssize_t
843
send_txtime(UA_EventLoopPOSIX *el, ETH_FD *conn, const UA_KeyValueMap *params,
844
0
            UA_DateTime txtime, const char *bytes, size_t bytesSize) {
845
    /* Get additiona parameters */
846
0
    const UA_UInt16 *txtime_pico = (const UA_UInt16*)
847
0
        UA_KeyValueMap_getScalar(params,
848
0
                                 ethConnectionParams[ETH_PARAMINDEX_TXTIME_PICO].name,
849
0
                                 &UA_TYPES[UA_TYPES_UINT16]);
850
0
    const UA_Boolean *txtime_drop = (const UA_Boolean*)
851
0
        UA_KeyValueMap_getScalar(params,
852
0
                                 ethConnectionParams[ETH_PARAMINDEX_TXTIME_DROP].name,
853
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
854
0
#ifndef SCM_DROP_IF_LATE
855
0
    if(txtime_drop) {
856
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
857
0
                     "ETH %u\t| txtime drop_if_late not supported on the current system",
858
0
                     (unsigned)conn->rfd.fd);
859
0
        return 0;
860
0
    }
861
0
#endif
862
863
864
    /* Transform from 100ns since 1601 to ns since the Unix Epoch */
865
0
    UA_UInt64 transmission_time = (UA_UInt64)
866
0
        (txtime - UA_DATETIME_UNIX_EPOCH) * 100;
867
0
    if(txtime_pico)
868
0
        transmission_time += (*txtime_pico) / 1000;
869
870
    /* Structure for scattering or gathering of input/output */
871
0
    struct iovec inputOutputVec;
872
0
    inputOutputVec.iov_base = (void*)(uintptr_t)bytes;
873
0
    inputOutputVec.iov_len  = bytesSize;
874
875
    /* Specify the transmission time in the CMSG. */
876
0
    char dataPacket[CMSG_SPACE(sizeof(uint64_t))
877
#ifdef SCM_DROP_IF_LATE
878
                    + CMSG_SPACE(sizeof(uint8_t))
879
#endif
880
0
                    ];
881
0
    struct msghdr message;
882
0
    memset(&message, 0, sizeof(struct msghdr));
883
0
    message.msg_control    = dataPacket;
884
0
    message.msg_controllen = sizeof(dataPacket);
885
0
    message.msg_name       = (struct sockaddr*)&conn->sll;
886
0
    message.msg_namelen    = sizeof(conn->sll);
887
0
    message.msg_iov        = &inputOutputVec;
888
0
    message.msg_iovlen     = 1;
889
890
0
    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&message);
891
0
    cmsg->cmsg_level = SOL_SOCKET;
892
0
    cmsg->cmsg_type  = SCM_TXTIME;
893
0
    cmsg->cmsg_len   = CMSG_LEN(sizeof(__u64));
894
0
    *((__u64*)CMSG_DATA(cmsg)) = transmission_time;
895
896
#ifdef SCM_DROP_IF_LATE
897
    cmsg = CMSG_NXTHDR(&message, cmsg);
898
    cmsg->cmsg_level = SOL_SOCKET;
899
    cmsg->cmsg_type = SCM_DROP_IF_LATE;
900
    cmsg->cmsg_len = CMSG_LEN(sizeof(uint8_t));
901
    *((uint8_t*)CMSG_DATA(cmsg)) = (!txtime_drop || *txtime_drop) ? 1: 0;
902
#endif
903
904
    /* Send */
905
0
    return sendmsg(conn->rfd.fd, &message, 0);
906
0
}
907
#endif
908
909
static UA_StatusCode
910
ETH_sendWithConnection(UA_ConnectionManager *cm, uintptr_t connectionId,
911
0
                       const UA_KeyValueMap *params, UA_ByteString *buf) {
912
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
913
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
914
915
0
    UA_LOCK(&el->elMutex);
916
917
    /* Get the ETH_FD */
918
0
    UA_FD fd = (UA_FD)connectionId;
919
0
    ETH_FD *conn = (ETH_FD*)ZIP_FIND(UA_FDTree, &pcm->fds, &fd);
920
0
    if(!conn) {
921
0
        UA_UNLOCK(&el->elMutex);
922
        /* The fixed allocation prefix can be recovered without the vanished
923
         * connection. */
924
0
        ETH_freeNetworkBuffer(cm, connectionId, buf);
925
0
        return UA_STATUSCODE_BADCONNECTIONREJECTED;
926
0
    }
927
    /* The connection may be finalized after the EventLoop lock is released.
928
     * Retain the only connection-specific value needed to restore the buffer. */
929
0
    const size_t headerSize = conn->headerSize;
930
931
    /* Uncover and set the Ethernet header */
932
0
    buf->data -= headerSize;
933
0
    buf->length += headerSize;
934
0
    memcpy(buf->data, conn->header, headerSize);
935
0
    if(conn->lengthOffset) {
936
0
        UA_UInt16 *ethLength =  (UA_UInt16*)&buf->data[conn->lengthOffset];
937
0
        *ethLength = htons((UA_UInt16)(buf->length - headerSize));
938
0
    }
939
940
    /* Was a txtime configured? */
941
0
    const UA_DateTime *txtime = (const UA_DateTime*)
942
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_TXTIME].name,
943
0
                                 &UA_TYPES[UA_TYPES_DATETIME]);
944
0
    if(txtime && !conn->txtimeEnabled) {
945
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
946
0
                     "ETH %u\t| txtime was not configured for the connection",
947
0
                     (unsigned)connectionId);
948
0
        UA_UNLOCK(&el->elMutex);
949
0
        ETH_freeSendBuffer(cm, connectionId, buf, headerSize);
950
0
        return UA_STATUSCODE_BADINTERNALERROR;
951
0
    }
952
953
    /* Prevent OS signals when sending to a closed socket */
954
0
    int flags = MSG_NOSIGNAL;
955
956
0
    struct pollfd tmp_poll_fd;
957
0
    tmp_poll_fd.fd = (UA_FD)connectionId;
958
0
    tmp_poll_fd.events = UA_POLLOUT;
959
960
    /* Send the full buffer. This may require several calls to send */
961
0
    size_t nWritten = 0;
962
0
    do {
963
0
        ssize_t n = 0;
964
0
        do {
965
0
            UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
966
0
                         "ETH %u\t| Attempting to send", (unsigned)connectionId);
967
0
            UA_RESET_ERRNO;
968
0
            size_t bytes_to_send = buf->length - nWritten;
969
0
#ifdef SO_TXTIME
970
0
            if(txtime) {
971
0
                n = send_txtime(el, conn, params, *txtime,
972
0
                                (const char*)buf->data + nWritten, bytes_to_send);
973
0
            } else
974
0
#endif
975
0
            {
976
0
                n = UA_sendto(conn->rfd.fd,
977
0
                              (const char*)buf->data + nWritten, bytes_to_send,
978
0
                              flags, (struct sockaddr*)&conn->sll, sizeof(conn->sll));
979
0
            }
980
0
            if(n < 0) {
981
                /* An error we cannot recover from? */
982
0
                if(UA_ERRNO != UA_INTERRUPTED &&
983
0
                   UA_ERRNO != UA_WOULDBLOCK &&
984
0
                   UA_ERRNO != UA_AGAIN) {
985
0
                    UA_LOG_SOCKET_ERRNO_WRAP(
986
0
                       UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
987
0
                                    "ETH %u\t| Send failed with error %s",
988
0
                                    (unsigned)connectionId, errno_str));
989
0
                    ETH_shutdown(pcm, conn);
990
0
                    UA_UNLOCK(&el->elMutex);
991
0
                    ETH_freeSendBuffer(cm, connectionId, buf, headerSize);
992
0
                    return UA_STATUSCODE_BADCONNECTIONCLOSED;
993
0
                }
994
995
                /* Poll for the socket resources to become available and retry
996
                 * (blocking) */
997
0
                int poll_ret;
998
0
                do {
999
0
                    UA_RESET_ERRNO;
1000
0
                    poll_ret = UA_poll(&tmp_poll_fd, 1, 100);
1001
0
                    if(poll_ret < 0 && UA_ERRNO != UA_INTERRUPTED) {
1002
0
                        UA_LOG_SOCKET_ERRNO_WRAP(
1003
0
                           UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
1004
0
                                        "ETH %u\t| Send failed with error %s",
1005
0
                                        (unsigned)connectionId, errno_str));
1006
0
                        ETH_shutdown(pcm, conn);
1007
0
                        UA_UNLOCK(&el->elMutex);
1008
0
                        ETH_freeSendBuffer(cm, connectionId, buf, headerSize);
1009
0
                        return UA_STATUSCODE_BADCONNECTIONCLOSED;
1010
0
                    }
1011
0
                } while(poll_ret <= 0);
1012
0
            }
1013
0
        } while(n < 0);
1014
0
        nWritten += (size_t)n;
1015
0
    } while(nWritten < buf->length);
1016
1017
    /* Free the buffer */
1018
0
    UA_UNLOCK(&el->elMutex);
1019
0
    ETH_freeSendBuffer(cm, connectionId, buf, headerSize);
1020
0
    return UA_STATUSCODE_GOOD;
1021
0
}
1022
1023
static UA_StatusCode
1024
20.6k
ETH_eventSourceStart(UA_ConnectionManager *cm) {
1025
20.6k
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
1026
20.6k
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
1027
20.6k
    UA_LOCK(&el->elMutex);
1028
1029
    /* Check the state */
1030
20.6k
    if(cm->eventSource.state != UA_EVENTSOURCESTATE_STOPPED) {
1031
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
1032
0
                     "To start the Ethernet ConnectionManager, "
1033
0
                     "it has to be registered in an EventLoop and not started");
1034
0
        UA_UNLOCK(&el->elMutex);
1035
0
        return UA_STATUSCODE_BADINTERNALERROR;
1036
0
    }
1037
1038
    /* Check the parameters */
1039
20.6k
    UA_StatusCode res =
1040
20.6k
        UA_KeyValueRestriction_validate(el->eventLoop.logger, "ETH",
1041
20.6k
                                        ethManagerParams, ETH_MANAGERPARAMS,
1042
20.6k
                                        &cm->eventSource.params);
1043
20.6k
    if(res != UA_STATUSCODE_GOOD)
1044
0
        goto finish;
1045
1046
    /* Allocate the rx buffer */
1047
20.6k
    res = UA_EventLoopPOSIX_allocateStaticBuffers(pcm);
1048
20.6k
    if(res != UA_STATUSCODE_GOOD)
1049
0
        goto finish;
1050
1051
    /* Set the EventSource to the started state */
1052
20.6k
    cm->eventSource.state = UA_EVENTSOURCESTATE_STARTED;
1053
1054
20.6k
 finish:
1055
20.6k
    UA_UNLOCK(&el->elMutex);
1056
20.6k
    return res;
1057
20.6k
}
1058
1059
static void *
1060
0
ETH_shutdownCB(void *application, UA_RegisteredFD *rfd) {
1061
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)application;
1062
0
    ETH_shutdown(pcm, (ETH_FD*)rfd);
1063
0
    return NULL;
1064
0
}
1065
1066
static void
1067
20.6k
ETH_eventSourceStop(UA_ConnectionManager *cm) {
1068
20.6k
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
1069
20.6k
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
1070
20.6k
    UA_LOCK(&el->elMutex);
1071
1072
20.6k
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
1073
20.6k
                 "ETH\t| Shutting down the ConnectionManager");
1074
1075
    /* Prevent new connections to open */
1076
20.6k
    cm->eventSource.state = UA_EVENTSOURCESTATE_STOPPING;
1077
1078
    /* Shutdown all existing connection */
1079
20.6k
    ZIP_ITER(UA_FDTree, &pcm->fds, ETH_shutdownCB, cm);
1080
1081
    /* Check if stopped once more (also checking inside ETH_close, but there we
1082
     * don't check if there is no rfd at all) */
1083
20.6k
    ETH_checkStopped(pcm);
1084
1085
20.6k
    UA_UNLOCK(&el->elMutex);
1086
20.6k
}
1087
1088
static UA_StatusCode
1089
20.6k
ETH_eventSourceDelete(UA_ConnectionManager *cm) {
1090
20.6k
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
1091
20.6k
    if(cm->eventSource.state >= UA_EVENTSOURCESTATE_STARTING) {
1092
0
        UA_LOG_ERROR(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP,
1093
0
                     "ETH\t| The EventSource must be stopped before it can be deleted");
1094
0
        return UA_STATUSCODE_BADINTERNALERROR;
1095
0
    }
1096
1097
20.6k
    UA_KeyValueMap_clear(&cm->eventSource.params);
1098
20.6k
    UA_ByteString_clear(&pcm->rxBuffer);
1099
20.6k
    UA_ByteString_clear(&pcm->txBuffer);
1100
20.6k
    UA_String_clear(&cm->eventSource.name);
1101
20.6k
    UA_free(cm);
1102
20.6k
    return UA_STATUSCODE_GOOD;
1103
20.6k
}
1104
1105
static const char *ethName = "eth";
1106
1107
UA_ConnectionManager *
1108
20.6k
UA_ConnectionManager_new_POSIX_Ethernet(const UA_String eventSourceName) {
1109
20.6k
    UA_POSIXConnectionManager *cm = (UA_POSIXConnectionManager*)
1110
20.6k
        UA_calloc(1, sizeof(UA_POSIXConnectionManager));
1111
20.6k
    if(!cm)
1112
0
        return NULL;
1113
1114
20.6k
    cm->cm.eventSource.eventSourceType = UA_EVENTSOURCETYPE_CONNECTIONMANAGER;
1115
20.6k
    UA_String_copy(&eventSourceName, &cm->cm.eventSource.name);
1116
20.6k
    cm->cm.eventSource.start = (UA_StatusCode (*)(UA_EventSource *))ETH_eventSourceStart;
1117
20.6k
    cm->cm.eventSource.stop = (void (*)(UA_EventSource *))ETH_eventSourceStop;
1118
20.6k
    cm->cm.eventSource.free = (UA_StatusCode (*)(UA_EventSource *))ETH_eventSourceDelete;
1119
20.6k
    cm->cm.protocol = UA_STRING((char*)(uintptr_t)ethName);
1120
20.6k
    cm->cm.openConnection = ETH_openConnection;
1121
20.6k
    cm->cm.allocNetworkBuffer = ETH_allocNetworkBuffer;
1122
20.6k
    cm->cm.freeNetworkBuffer = ETH_freeNetworkBuffer;
1123
20.6k
    cm->cm.sendWithConnection = ETH_sendWithConnection;
1124
20.6k
    cm->cm.closeConnection = ETH_shutdownConnection;
1125
20.6k
    return &cm->cm;
1126
20.6k
}
1127
1128
#endif /* defined(UA_ARCHITECTURE_POSIX) && defined(__linux__) */