Coverage Report

Created: 2025-11-24 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541/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
502
#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
#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 */
192
0
    if(vid > 0 && vid != ETH_P_ALL) {
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
0
    UA_StatusCode res =
222
0
        UA_EventLoopPOSIX_allocNetworkBuffer(cm, connectionId, buf,
223
0
                                             bufSize + erfd->headerSize);
224
0
    if(UA_LIKELY(res == UA_STATUSCODE_GOOD)) {
225
0
        buf->data   += erfd->headerSize;
226
0
        buf->length -= erfd->headerSize;
227
0
    }
228
0
    return res;
229
0
}
230
231
static void
232
ETH_freeNetworkBuffer(UA_ConnectionManager *cm, uintptr_t connectionId,
233
0
                      UA_ByteString *buf) {
234
    /* Get the ETH_FD */
235
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
236
0
    UA_FD fd = (UA_FD)connectionId;
237
0
    ETH_FD *erfd = (ETH_FD*)ZIP_FIND(UA_FDTree, &pcm->fds, &fd);
238
0
    if(!erfd)
239
0
        return;
240
241
    /* Unhide the Ethernet header and free */
242
0
    buf->data   -= erfd->headerSize;
243
0
    buf->length += erfd->headerSize;
244
0
    UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf);
245
0
}
246
247
/* Test if the ConnectionManager can be stopped */
248
static void
249
502
ETH_checkStopped(UA_POSIXConnectionManager *pcm) {
250
502
    UA_LOCK_ASSERT(&((UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop)->elMutex);
251
252
502
    if(pcm->fdsSize == 0 &&
253
502
       pcm->cm.eventSource.state == UA_EVENTSOURCESTATE_STOPPING) {
254
502
        UA_LOG_DEBUG(pcm->cm.eventSource.eventLoop->logger, UA_LOGCATEGORY_NETWORK,
255
502
                     "ETH\t| All sockets closed, the EventLoop has stopped");
256
502
        pcm->cm.eventSource.state = UA_EVENTSOURCESTATE_STOPPED;
257
502
    }
258
502
}
259
260
/* This method must not be called from the application directly, but from within
261
 * the EventLoop. Otherwise we cannot be sure whether the file descriptor is
262
 * still used after calling close. */
263
static void
264
0
ETH_close(UA_POSIXConnectionManager *pcm, ETH_FD *conn) {
265
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
266
0
    UA_LOCK_ASSERT(&el->elMutex);
267
268
0
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
269
0
                 "ETH %u\t| Closing connection",
270
0
                 (unsigned)conn->rfd.fd);
271
272
    /* Deregister from the EventLoop */
273
0
    UA_EventLoopPOSIX_deregisterFD(el, &conn->rfd);
274
275
    /* Deregister internally */
276
0
    ZIP_REMOVE(UA_FDTree, &pcm->fds, &conn->rfd);
277
0
    UA_assert(pcm->fdsSize > 0);
278
0
    pcm->fdsSize--;
279
280
    /* Signal closing to the application */
281
0
    conn->applicationCB(&pcm->cm, (uintptr_t)conn->rfd.fd,
282
0
                        conn->application, &conn->context,
283
0
                        UA_CONNECTIONSTATE_CLOSING,
284
0
                        &UA_KEYVALUEMAP_NULL, UA_BYTESTRING_NULL);
285
286
    /* Close the socket */
287
0
    UA_RESET_ERRNO;
288
0
    int ret = UA_close(conn->rfd.fd);
289
0
    if(ret == 0) {
290
0
        UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
291
0
                    "ETH %u\t| Socket closed", (unsigned)conn->rfd.fd);
292
0
    } else {
293
0
        UA_LOG_SOCKET_ERRNO_WRAP(
294
0
           UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
295
0
                          "ETH %u\t| Could not close the socket (%s)",
296
0
                          (unsigned)conn->rfd.fd, errno_str));
297
0
    }
298
299
    /* Don't call free here. This might be done automatically via the delayed
300
     * callback that calls ETH_close. */
301
    /* UA_free(rfd); */
302
303
    /* Stop if the ucm is stopping and this was the last open socket */
304
0
    ETH_checkStopped(pcm);
305
0
}
306
307
static void
308
0
ETH_delayedClose(void *application, void *context) {
309
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)application;
310
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
311
0
    ETH_FD *conn = (ETH_FD*)context;
312
0
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
313
0
                 "ETH %u\t| Delayed closing of the connection",
314
0
                 (unsigned)conn->rfd.fd);
315
0
    UA_LOCK(&el->elMutex);
316
0
    ETH_close(pcm, conn);
317
0
    UA_UNLOCK(&el->elMutex);
318
0
    UA_free(conn);
319
0
}
320
321
/* Gets called when a socket receives data or closes */
322
static void
323
ETH_connectionSocketCallback(UA_ConnectionManager *cm, UA_RegisteredFD *rfd,
324
0
                             short event) {
325
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
326
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
327
0
    UA_LOCK_ASSERT(&el->elMutex);
328
329
0
    ETH_FD *conn = (ETH_FD*)rfd;
330
0
    if(event == UA_FDEVENT_ERR) {
331
0
        UA_LOG_SOCKET_ERRNO_WRAP(
332
0
           UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
333
0
                        "ETH %u\t| recv signaled the socket was shutdown (%s)",
334
0
                        (unsigned)rfd->fd, errno_str));
335
0
        ETH_close(pcm, conn);
336
0
        UA_free(rfd);
337
0
        return;
338
0
    }
339
340
    /* Use the already allocated receive-buffer */
341
0
    UA_ByteString response = pcm->rxBuffer;
342
343
    /* Receive */
344
0
    UA_RESET_ERRNO;
345
0
#ifndef UA_ARCHITECTURE_WIN32
346
0
    ssize_t ret = UA_recv(rfd->fd, (char*)response.data,
347
0
                          response.length, MSG_DONTWAIT);
348
#else
349
    int ret = UA_recv(rfd->fd, (char*)response.data,
350
                      response.length, MSG_DONTWAIT);
351
#endif
352
353
    /* Receive has failed */
354
0
    if(ret <= 0) {
355
0
        if(UA_ERRNO == UA_INTERRUPTED)
356
0
            return;
357
358
        /* Orderly shutdown of the socket. We can immediately close as no method
359
         * "below" in the call stack will use the socket in this iteration of
360
         * the EventLoop. */
361
0
        UA_LOG_SOCKET_ERRNO_WRAP(
362
0
           UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
363
0
                        "ETH %u\t| recv signaled the socket was shutdown (%s)",
364
0
                        (unsigned)rfd->fd, errno_str));
365
0
        ETH_close(pcm, conn);
366
0
        UA_free(rfd);
367
0
        return;
368
0
    }
369
370
0
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
371
0
                 "ETH %u\t| Received message of size %u",
372
0
                 (unsigned)rfd->fd, (unsigned)ret);
373
374
0
    response.length = (size_t)ret;
375
376
    /* Parse the Ethernet header */
377
0
    unsigned char destAddr[ETHER_ADDR_LEN];
378
0
    unsigned char sourceAddr[ETHER_ADDR_LEN];
379
0
    UA_UInt16 etherType = 0;
380
0
    UA_UInt16 vid = 0;
381
0
    UA_Byte pcp = 0;
382
0
    UA_Boolean dei = 0;
383
0
    size_t headerSize = parseETHHeader(&response, destAddr, sourceAddr,
384
0
                                       &etherType, &vid, &pcp, &dei);
385
0
    if(headerSize == 0)
386
0
        return;
387
388
    /* Set up the parameter arguments passed to the application */
389
0
    unsigned char destAddrBytes[18];
390
0
    unsigned char sourceAddrBytes[18];
391
0
    setAddrString(destAddrBytes, destAddr);
392
0
    setAddrString(sourceAddrBytes, sourceAddr);
393
0
    UA_String destAddrStr = {17, destAddrBytes};
394
0
    UA_String sourceAddrStr = {17, sourceAddrBytes};
395
396
0
    size_t paramsSize = 2;
397
0
    UA_KeyValuePair params[6];
398
0
    params[0].key = UA_QUALIFIEDNAME(0, "destination-address");
399
0
    UA_Variant_setScalar(&params[0].value, &destAddrStr, &UA_TYPES[UA_TYPES_STRING]);
400
0
    params[1].key = UA_QUALIFIEDNAME(0, "source-address");
401
0
    UA_Variant_setScalar(&params[1].value, &sourceAddrStr, &UA_TYPES[UA_TYPES_STRING]);
402
403
0
    if(etherType > 0) {
404
0
        params[2].key = UA_QUALIFIEDNAME(0, "ethertype");
405
0
        UA_Variant_setScalar(&params[1].value, &etherType, &UA_TYPES[UA_TYPES_UINT16]);
406
0
        paramsSize++;
407
0
    }
408
409
0
    if(vid > 0) {
410
0
        params[paramsSize].key = UA_QUALIFIEDNAME(0, "vid");
411
0
        UA_Variant_setScalar(&params[paramsSize].value, &vid, &UA_TYPES[UA_TYPES_UINT16]);
412
0
        params[paramsSize+1].key = UA_QUALIFIEDNAME(0, "pcp");
413
0
        UA_Variant_setScalar(&params[paramsSize+1].value, &pcp, &UA_TYPES[UA_TYPES_BYTE]);
414
0
        params[paramsSize+2].key = UA_QUALIFIEDNAME(0, "dei");
415
0
        UA_Variant_setScalar(&params[paramsSize+2].value, &dei, &UA_TYPES[UA_TYPES_BOOLEAN]);
416
0
        paramsSize += 3;
417
0
    }
418
419
    /* Callback to the application layer with the Ethernet header hidden */
420
0
    UA_KeyValueMap map = {paramsSize, params};
421
0
    response.data += headerSize;
422
0
    response.length -= headerSize;
423
0
    conn->applicationCB(cm, (uintptr_t)rfd->fd, conn->application, &conn->context,
424
0
                        UA_CONNECTIONSTATE_ESTABLISHED, &map, response);
425
0
    response.data -= headerSize;
426
0
    response.length += headerSize;
427
0
}
428
429
static UA_StatusCode
430
ETH_openListenConnection(UA_EventLoopPOSIX *el, ETH_FD *conn,
431
                         const UA_KeyValueMap *params,
432
                         int ifindex, UA_UInt16 etherType,
433
0
                         UA_Boolean validate) {
434
0
    UA_LOCK_ASSERT(&el->elMutex);
435
436
    /* Bind the socket to interface and EtherType. Don't receive anything else. */
437
0
    struct sockaddr_ll sll;
438
0
    memset(&sll, 0, sizeof(struct sockaddr_ll));
439
0
    sll.sll_family = AF_PACKET;
440
0
    sll.sll_protocol = htons(etherType);
441
0
    sll.sll_ifindex = ifindex;
442
0
    if(!validate && bind(conn->rfd.fd, (struct sockaddr*)&sll, sizeof(sll)) < 0)
443
0
        return UA_STATUSCODE_BADINTERNALERROR;
444
445
    /* Immediately register for listen events. Don't have to wait for a
446
     * connection to open. */
447
0
    conn->rfd.listenEvents = UA_FDEVENT_IN;
448
449
    /* Set receiving to promiscuous (all target host addresses) */
450
0
    UA_RESET_ERRNO;
451
0
    const UA_Boolean *promiscuous = (const UA_Boolean*)
452
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_PROMISCUOUS].name,
453
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
454
0
    if(promiscuous && *promiscuous) {
455
0
        struct packet_mreq mreq;
456
0
        memset(&mreq, 0, sizeof(struct packet_mreq));
457
0
        mreq.mr_ifindex = ifindex;
458
0
        mreq.mr_type = PACKET_MR_PROMISC;
459
0
        int ret = UA_setsockopt(conn->rfd.fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
460
0
                             &mreq, sizeof(mreq));
461
0
        if(ret < 0) {
462
0
            UA_LOG_SOCKET_ERRNO_WRAP(
463
0
               UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
464
0
                            "ETH %u\t| Could not set raw socket to promiscuous mode %s",
465
0
                            (unsigned)conn->rfd.fd, errno_str));
466
0
            return UA_STATUSCODE_BADINTERNALERROR;
467
0
        } else {
468
0
            UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
469
0
                        "ETH %u\t| The socket was set to promiscuous mode",
470
0
                        (unsigned)conn->rfd.fd);
471
0
        }
472
0
    }
473
474
    /* Register for multicast if an address is defined */
475
0
    const UA_String *address = (const UA_String*)
476
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_ADDR].name,
477
0
                                 &UA_TYPES[UA_TYPES_STRING]);
478
0
    if(address) {
479
0
        UA_Byte addr[ETHER_ADDR_LEN];
480
0
        UA_StatusCode res = parseEthAddress(address, addr);
481
0
        if(res != UA_STATUSCODE_GOOD) {
482
0
            UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
483
0
                         "ETH\t| Address for listening cannot be parsed");
484
0
            return res;
485
0
        }
486
487
0
        if(!isMulticastEthAddress(addr)) {
488
0
            UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
489
0
                         "ETH\t| Address for listening is not a multicast address. Ignoring.");
490
0
            return UA_STATUSCODE_GOOD;
491
0
        }
492
493
0
        UA_RESET_ERRNO;
494
0
        struct packet_mreq mreq;
495
0
        memset(&mreq, 0, sizeof(struct packet_mreq));
496
0
        mreq.mr_ifindex = ifindex;
497
0
        mreq.mr_type = PACKET_MR_MULTICAST;
498
0
        mreq.mr_alen = ETH_ALEN;
499
0
        memcpy(mreq.mr_address, addr, ETHER_ADDR_LEN);
500
0
        if(!validate && UA_setsockopt(conn->rfd.fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
501
0
                                      (char *)&mreq, sizeof(mreq)) < 0) {
502
0
            UA_LOG_SOCKET_ERRNO_WRAP(
503
0
               UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
504
0
                            "ETH\t| Registering for multicast failed with error %s",
505
0
                            errno_str));
506
0
            return UA_STATUSCODE_BADINTERNALERROR;
507
0
        }
508
0
    }
509
510
0
    UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
511
0
                "ETH %u\t| Opened an Ethernet listen socket",
512
0
                (unsigned)conn->rfd.fd);
513
514
0
    return UA_STATUSCODE_GOOD;
515
0
}
516
517
static UA_StatusCode
518
ETH_openSendConnection(UA_EventLoopPOSIX *el, ETH_FD *conn, const UA_KeyValueMap *params,
519
0
                       UA_Byte source[ETHER_ADDR_LEN], int ifindex, UA_UInt16 etherType) {
520
0
    UA_LOCK_ASSERT(&el->elMutex);
521
522
    /* Parse the target address (has to exist) */
523
0
    const UA_String *address = (const UA_String*)
524
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_ADDR].name,
525
0
                                 &UA_TYPES[UA_TYPES_STRING]);
526
0
    UA_Byte dest[ETHER_ADDR_LEN];
527
0
    UA_StatusCode res = parseEthAddress(address, dest);
528
0
    if(res != UA_STATUSCODE_GOOD) {
529
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
530
0
                     "ETH\t| Could not parse the Ethernet address \"%.*s\"",
531
0
                     (int)address->length, (char*)address->data);
532
0
        return res;
533
0
    }
534
535
    /* Get the VLAN config */
536
0
    UA_UInt16 vid = 0;
537
0
    UA_Byte pcp = 0;
538
0
    UA_Boolean eid = false;
539
540
0
    const UA_UInt16 *vidp = (const UA_UInt16*)
541
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_VID].name,
542
0
                                 &UA_TYPES[UA_TYPES_UINT16]);
543
0
    if(vidp)
544
0
        vid = *vidp;
545
546
0
    const UA_Byte *pcpp = (const UA_Byte*)
547
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_PCP].name,
548
0
                                 &UA_TYPES[UA_TYPES_BYTE]);
549
0
    if(pcpp)
550
0
        pcp = *pcpp;
551
552
0
    const UA_Boolean *eidp = (const UA_Boolean*)
553
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_DEI].name,
554
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
555
0
    if(eidp)
556
0
        eid = *eidp;
557
558
    /* Store the structure for sendto */
559
0
    conn->sll.sll_ifindex = ifindex;
560
0
    conn->sll.sll_halen = ETH_ALEN;
561
0
    memcpy(conn->sll.sll_addr, dest, ETHER_ADDR_LEN);
562
563
    /* Generate the Ethernet header */
564
0
    conn->headerSize = setETHHeader(conn->header, dest, source, etherType,
565
0
                                    vid, pcp, eid, &conn->lengthOffset);
566
567
    /* Set the send priority if defined */
568
0
    const UA_Int32 *soPriority = (const UA_Int32*)
569
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_PRIORITY].name,
570
0
                                 &UA_TYPES[UA_TYPES_INT32]);
571
0
    if(soPriority) {
572
0
        UA_RESET_ERRNO;
573
0
        int prioRes = UA_setsockopt(conn->rfd.fd, SOL_SOCKET, SO_PRIORITY,
574
0
                                 soPriority, sizeof(int));
575
0
        if(prioRes != 0) {
576
0
            UA_LOG_SOCKET_ERRNO_WRAP(
577
0
               UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
578
0
                            "UA_setsockopt SO_PRIORITY failed with error %s", errno_str));
579
0
            return UA_STATUSCODE_BADINTERNALERROR;
580
0
        }
581
0
    }
582
583
    /* Enable txtime sending */
584
0
    const UA_Boolean *txtime_enable = (const UA_Boolean*)
585
0
        UA_KeyValueMap_getScalar(params,
586
0
                                 ethConnectionParams[ETH_PARAMINDEX_TXTIME_ENABLE].name,
587
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
588
589
0
    if(txtime_enable && *txtime_enable) {
590
#ifndef SO_TXTIME
591
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
592
                       "ETH %u\t| txtime feature not supported",
593
                       (unsigned)conn->rfd.fd);
594
#else
595
0
        const UA_UInt32 *txtime_flags = (const UA_UInt32*)
596
0
        UA_KeyValueMap_getScalar(params,
597
0
                                 ethConnectionParams[ETH_PARAMINDEX_TXTIME_FLAGS].name,
598
0
                                 &UA_TYPES[UA_TYPES_UINT32]);
599
0
        UA_RESET_ERRNO;
600
0
        struct sock_txtime so_txtime_val;
601
0
        memset(&so_txtime_val, 0, sizeof(struct sock_txtime));
602
0
        so_txtime_val.clockid = el->clockSourceMonotonic;
603
0
        so_txtime_val.flags = SOF_TXTIME_REPORT_ERRORS;
604
0
        if(txtime_flags)
605
0
            so_txtime_val.flags = *txtime_flags;
606
0
        if(UA_setsockopt(conn->rfd.fd, SOL_SOCKET, SO_TXTIME,
607
0
                      &so_txtime_val, sizeof(so_txtime_val)) == 0) {
608
0
            conn->txtimeEnabled = true;
609
0
        } else {
610
0
            UA_LOG_SOCKET_ERRNO_WRAP(
611
0
               UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
612
0
                              "ETH %u\t| Could not enable txtime (%s)",
613
0
                              (unsigned)conn->rfd.fd, errno_str));
614
0
        }
615
0
#endif
616
0
    }
617
618
    /* Done creating the socket */
619
0
    UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
620
0
                "ETH %u\t| Opened an Ethernet send socket",
621
0
                (unsigned)conn->rfd.fd);
622
623
0
    return UA_STATUSCODE_GOOD;
624
0
}
625
626
static UA_StatusCode
627
ETH_openConnection(UA_ConnectionManager *cm, const UA_KeyValueMap *params,
628
                   void *application, void *context,
629
0
                   UA_ConnectionManager_connectionCallback connectionCallback) {
630
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
631
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX *)cm->eventSource.eventLoop;
632
633
0
    UA_LOCK(&el->elMutex);
634
635
    /* Listen or send connection? */
636
0
    const UA_Boolean *listen = (const UA_Boolean*)
637
0
        UA_KeyValueMap_getScalar(params,
638
0
                                 ethConnectionParams[ETH_PARAMINDEX_LISTEN].name,
639
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
640
0
    size_t ethParamRestrictions = ETH_PARAMETERSSIZE;
641
0
    if(!listen || !*listen)
642
0
        ethParamRestrictions++; /* Use the last restriction only for send connections */
643
644
    /* Validate the parameters */
645
0
    UA_StatusCode res =
646
0
        UA_KeyValueRestriction_validate(el->eventLoop.logger, "ETH", ethConnectionParams,
647
0
                                        ethParamRestrictions, params);
648
0
    if(res != UA_STATUSCODE_GOOD) {
649
0
        UA_UNLOCK(&el->elMutex);
650
0
        return res;
651
0
    }
652
653
    /* Only validate the parameters? */
654
0
    UA_Boolean validate = false;
655
0
    const UA_Boolean *validateParam = (const UA_Boolean*)
656
0
        UA_KeyValueMap_getScalar(params,
657
0
                                 ethConnectionParams[ETH_PARAMINDEX_VALIDATE].name,
658
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
659
0
    if(validateParam)
660
0
        validate = *validateParam;
661
662
    /* Get the EtherType parameter */
663
0
    UA_UInt16 etherType = ETH_P_ALL;
664
0
    const UA_UInt16 *etParam =  (const UA_UInt16*)
665
0
        UA_KeyValueMap_getScalar(params,
666
0
                                 ethConnectionParams[ETH_PARAMINDEX_ETHERTYPE].name,
667
0
                                 &UA_TYPES[UA_TYPES_UINT16]);
668
0
    if(etParam)
669
0
        etherType = *etParam;
670
671
    /* Get the interface index */
672
0
    const UA_String *interface = (const UA_String*)
673
0
        UA_KeyValueMap_getScalar(params,
674
0
                                 ethConnectionParams[ETH_PARAMINDEX_IFACE].name,
675
0
                                 &UA_TYPES[UA_TYPES_STRING]);
676
0
    if(interface->length >= 128) {
677
0
        UA_UNLOCK(&el->elMutex);
678
0
        return UA_STATUSCODE_BADINTERNALERROR;
679
0
    }
680
0
    char ifname[128];
681
0
    memcpy(ifname, interface->data, interface->length);
682
0
    ifname[interface->length] = 0;
683
0
    int ifindex = (int)if_nametoindex(ifname);
684
0
    if(ifindex == 0) {
685
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
686
0
                     "ETH\t| Could not find the interface %s", ifname);
687
0
        UA_UNLOCK(&el->elMutex);
688
0
        return UA_STATUSCODE_BADINTERNALERROR;
689
0
    }
690
691
    /* Create the socket and add the basic configuration */
692
0
    ETH_FD *conn = NULL;
693
0
    UA_FD sockfd;
694
0
    if(listen && *listen)
695
0
        sockfd = UA_socket(PF_PACKET, SOCK_RAW, htons(etherType));
696
0
    else
697
0
        sockfd = UA_socket(PF_PACKET, SOCK_RAW, 0); /* Don't receive */
698
0
    if(sockfd == -1) {
699
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
700
0
                     "ETH\t| Could not create a raw Ethernet socket (are you root?)");
701
0
        UA_UNLOCK(&el->elMutex);
702
0
        return UA_STATUSCODE_BADINTERNALERROR;
703
0
    }
704
705
    /* SO_REUSEADDR is unnecessary (and unsupported on some Linux systems):
706
     * res |= UA_EventLoopPOSIX_setReusable(sockfd); */
707
0
    res |= UA_EventLoopPOSIX_setNonBlocking(sockfd);
708
0
    res |= UA_EventLoopPOSIX_setNoSigPipe(sockfd);
709
0
    if(res != UA_STATUSCODE_GOOD)
710
0
        goto cleanup;
711
712
    /* Create the FD object */
713
0
    conn = (ETH_FD*)UA_calloc(1, sizeof(ETH_FD));
714
0
    if(!conn) {
715
0
        res = UA_STATUSCODE_BADOUTOFMEMORY;
716
0
        goto cleanup;
717
0
    }
718
719
0
    conn->rfd.fd = sockfd;
720
0
    conn->rfd.es = &pcm->cm.eventSource;
721
0
    conn->rfd.eventSourceCB = (UA_FDCallback)ETH_connectionSocketCallback;
722
0
    conn->context = context;
723
0
    conn->application = application;
724
0
    conn->applicationCB = connectionCallback;
725
726
    /* Configure a listen or a send connection */
727
0
    if(!listen || !*listen) {
728
        /* Get the source address for the interface */
729
0
        UA_RESET_ERRNO;
730
0
        struct ifreq ifr;
731
0
        memcpy(ifr.ifr_name, ifname, interface->length);
732
0
        ifr.ifr_name[interface->length] = 0;
733
0
        int result = ioctl(conn->rfd.fd, SIOCGIFHWADDR, &ifr);
734
0
        if(result == -1) {
735
0
            UA_LOG_SOCKET_ERRNO_WRAP(
736
0
               UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
737
0
                            "ETH %u\t| Cannot get the source address, %s",
738
0
                            (unsigned)conn->rfd.fd, errno_str));
739
0
            res = UA_STATUSCODE_BADCONNECTIONREJECTED;
740
0
            goto cleanup;
741
0
        }
742
0
        res = ETH_openSendConnection(el, conn, params,
743
0
                                     (unsigned char*)ifr.ifr_hwaddr.sa_data,
744
0
                                     ifindex, etherType);
745
0
    } else {
746
0
        res = ETH_openListenConnection(el, conn, params, ifindex, etherType, validate);
747
0
    }
748
749
    /* Don't actually open or shut down */
750
0
    if(validate || res != UA_STATUSCODE_GOOD)
751
0
        goto cleanup;
752
753
    /* Register in the EventLoop */
754
0
    res = UA_EventLoopPOSIX_registerFD(el, &conn->rfd);
755
0
    if(res != UA_STATUSCODE_GOOD)
756
0
        goto cleanup;
757
758
    /* Register locally */
759
0
    ZIP_INSERT(UA_FDTree, &pcm->fds, &conn->rfd);
760
0
    pcm->fdsSize++;
761
762
    /* Register the listen socket in the application */
763
0
    connectionCallback(cm, (uintptr_t)sockfd, application, &conn->context,
764
0
                       UA_CONNECTIONSTATE_ESTABLISHED, &UA_KEYVALUEMAP_NULL,
765
0
                       UA_BYTESTRING_NULL);
766
0
    UA_UNLOCK(&el->elMutex);
767
0
    return UA_STATUSCODE_GOOD;
768
769
0
 cleanup:
770
0
    UA_close(sockfd);
771
0
    UA_free(conn);
772
0
    UA_UNLOCK(&el->elMutex);
773
0
    return res;
774
0
}
775
776
static void
777
0
ETH_shutdown(UA_POSIXConnectionManager *pcm, ETH_FD *conn) {
778
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
779
0
    UA_LOCK_ASSERT(&((UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop)->elMutex);
780
781
0
    UA_DelayedCallback *dc = &conn->rfd.dc;
782
0
    if(dc->callback) {
783
0
        UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
784
0
                    "ETH %u\t| Cannot close - already closing",
785
0
                    (unsigned)conn->rfd.fd);
786
0
        return;
787
0
    }
788
789
    /* Shutdown the socket to cancel the current select/epoll */
790
0
    UA_shutdown(conn->rfd.fd, UA_SHUT_RDWR);
791
792
0
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
793
0
                 "ETH %u\t| Shutdown called", (unsigned)conn->rfd.fd);
794
795
0
    dc->callback = ETH_delayedClose;
796
0
    dc->application = pcm;
797
0
    dc->context = conn;
798
799
    /* Adding a delayed callback does not take a lock */
800
0
    UA_EventLoopPOSIX_addDelayedCallback((UA_EventLoop*)el, dc);
801
0
}
802
803
static UA_StatusCode
804
0
ETH_shutdownConnection(UA_ConnectionManager *cm, uintptr_t connectionId) {
805
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
806
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
807
0
    UA_LOCK(&el->elMutex);
808
809
    /* Get the ETH_FD */
810
0
    UA_FD fd = (UA_FD)connectionId;
811
0
    UA_RegisteredFD *rfd = ZIP_FIND(UA_FDTree, &pcm->fds, &fd);
812
0
    if(!rfd) {
813
0
        UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
814
0
                       "ETH\t| Cannot close Ethernet connection %u - not found",
815
0
                       (unsigned)connectionId);
816
0
        UA_UNLOCK(&el->elMutex);
817
0
        return UA_STATUSCODE_BADNOTFOUND;
818
0
    }
819
820
0
    ETH_shutdown(pcm, (ETH_FD*)rfd);
821
0
    UA_UNLOCK(&el->elMutex);
822
0
    return UA_STATUSCODE_GOOD;
823
0
}
824
825
#ifdef SO_TXTIME
826
static ssize_t
827
send_txtime(UA_EventLoopPOSIX *el, ETH_FD *conn, const UA_KeyValueMap *params,
828
0
            UA_DateTime txtime, const char *bytes, size_t bytesSize) {
829
    /* Get additiona parameters */
830
0
    const UA_UInt16 *txtime_pico = (const UA_UInt16*)
831
0
        UA_KeyValueMap_getScalar(params,
832
0
                                 ethConnectionParams[ETH_PARAMINDEX_TXTIME_PICO].name,
833
0
                                 &UA_TYPES[UA_TYPES_UINT16]);
834
0
    const UA_Boolean *txtime_drop = (const UA_Boolean*)
835
0
        UA_KeyValueMap_getScalar(params,
836
0
                                 ethConnectionParams[ETH_PARAMINDEX_TXTIME_DROP].name,
837
0
                                 &UA_TYPES[UA_TYPES_BOOLEAN]);
838
0
#ifndef SCM_DROP_IF_LATE
839
0
    if(txtime_drop) {
840
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
841
0
                     "ETH %u\t| txtime drop_if_late not supported on the current system",
842
0
                     (unsigned)conn->rfd.fd);
843
0
        return 0;
844
0
    }
845
0
#endif
846
847
848
    /* Transform from 100ns since 1601 to ns since the Unix Epoch */
849
0
    UA_UInt64 transmission_time = (UA_UInt64)
850
0
        (txtime - UA_DATETIME_UNIX_EPOCH) * 100;
851
0
    if(txtime_pico)
852
0
        transmission_time += (*txtime_pico) / 1000;
853
854
    /* Structure for scattering or gathering of input/output */
855
0
    struct iovec inputOutputVec;
856
0
    inputOutputVec.iov_base = (void*)(uintptr_t)bytes;
857
0
    inputOutputVec.iov_len  = bytesSize;
858
859
    /* Specify the transmission time in the CMSG. */
860
0
    char dataPacket[CMSG_SPACE(sizeof(uint64_t))
861
#ifdef SCM_DROP_IF_LATE
862
                    + CMSG_SPACE(sizeof(uint8_t))
863
#endif
864
0
                    ];
865
0
    struct msghdr message;
866
0
    memset(&message, 0, sizeof(struct msghdr));
867
0
    message.msg_control    = dataPacket;
868
0
    message.msg_controllen = sizeof(dataPacket);
869
0
    message.msg_name       = (struct sockaddr*)&conn->sll;
870
0
    message.msg_namelen    = sizeof(conn->sll);
871
0
    message.msg_iov        = &inputOutputVec;
872
0
    message.msg_iovlen     = 1;
873
874
0
    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&message);
875
0
    cmsg->cmsg_level = SOL_SOCKET;
876
0
    cmsg->cmsg_type  = SCM_TXTIME;
877
0
    cmsg->cmsg_len   = CMSG_LEN(sizeof(__u64));
878
0
    *((__u64*)CMSG_DATA(cmsg)) = transmission_time;
879
880
#ifdef SCM_DROP_IF_LATE
881
    cmsg = CMSG_NXTHDR(&message, cmsg);
882
    cmsg->cmsg_level = SOL_SOCKET;
883
    cmsg->cmsg_type = SCM_DROP_IF_LATE;
884
    cmsg->cmsg_len = CMSG_LEN(sizeof(uint8_t));
885
    *((uint8_t*)CMSG_DATA(cmsg)) = (!txtime_drop || *txtime_drop) ? 1: 0;
886
#endif
887
888
    /* Send */
889
0
    return sendmsg(conn->rfd.fd, &message, 0);
890
0
}
891
#endif
892
893
static UA_StatusCode
894
ETH_sendWithConnection(UA_ConnectionManager *cm, uintptr_t connectionId,
895
0
                       const UA_KeyValueMap *params, UA_ByteString *buf) {
896
0
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
897
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
898
899
0
    UA_LOCK(&el->elMutex);
900
901
    /* Get the ETH_FD */
902
0
    UA_FD fd = (UA_FD)connectionId;
903
0
    ETH_FD *conn = (ETH_FD*)ZIP_FIND(UA_FDTree, &pcm->fds, &fd);
904
0
    if(!conn) {
905
0
        UA_UNLOCK(&el->elMutex);
906
0
        UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf);
907
0
        return UA_STATUSCODE_BADCONNECTIONREJECTED;
908
0
    }
909
910
    /* Uncover and set the Ethernet header */
911
0
    buf->data -= conn->headerSize;
912
0
    buf->length += conn->headerSize;
913
0
    memcpy(buf->data, conn->header, conn->headerSize);
914
0
    if(conn->lengthOffset) {
915
0
        UA_UInt16 *ethLength =  (UA_UInt16*)&buf->data[conn->lengthOffset];
916
0
        *ethLength = htons((UA_UInt16)(buf->length - conn->headerSize));
917
0
    }
918
919
    /* Was a txtime configured? */
920
0
    const UA_DateTime *txtime = (const UA_DateTime*)
921
0
        UA_KeyValueMap_getScalar(params, ethConnectionParams[ETH_PARAMINDEX_TXTIME].name,
922
0
                                 &UA_TYPES[UA_TYPES_DATETIME]);
923
0
    if(txtime && !conn->txtimeEnabled) {
924
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
925
0
                     "ETH %u\t| txtime was not configured for the connection",
926
0
                     (unsigned)connectionId);
927
0
        UA_UNLOCK(&el->elMutex);
928
0
        UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf);
929
0
        return UA_STATUSCODE_BADINTERNALERROR;
930
0
    }
931
932
    /* Prevent OS signals when sending to a closed socket */
933
0
    int flags = MSG_NOSIGNAL;
934
935
0
    struct pollfd tmp_poll_fd;
936
0
    tmp_poll_fd.fd = (UA_FD)connectionId;
937
0
    tmp_poll_fd.events = UA_POLLOUT;
938
939
    /* Send the full buffer. This may require several calls to send */
940
0
    size_t nWritten = 0;
941
0
    do {
942
0
        ssize_t n = 0;
943
0
        do {
944
0
            UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
945
0
                         "ETH %u\t| Attempting to send", (unsigned)connectionId);
946
0
            UA_RESET_ERRNO;
947
0
            size_t bytes_to_send = buf->length - nWritten;
948
0
#ifdef SO_TXTIME
949
0
            if(txtime) {
950
0
                n = send_txtime(el, conn, params, *txtime,
951
0
                                (const char*)buf->data + nWritten, bytes_to_send);
952
0
            } else
953
0
#endif
954
0
            {
955
0
                n = UA_sendto(conn->rfd.fd,
956
0
                              (const char*)buf->data + nWritten, bytes_to_send,
957
0
                              flags, (struct sockaddr*)&conn->sll, sizeof(conn->sll));
958
0
            }
959
0
            if(n < 0) {
960
                /* An error we cannot recover from? */
961
0
                if(UA_ERRNO != UA_INTERRUPTED &&
962
0
                   UA_ERRNO != UA_WOULDBLOCK &&
963
0
                   UA_ERRNO != UA_AGAIN) {
964
0
                    UA_LOG_SOCKET_ERRNO_WRAP(
965
0
                       UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
966
0
                                    "ETH %u\t| Send failed with error %s",
967
0
                                    (unsigned)connectionId, errno_str));
968
0
                    ETH_shutdown(pcm, conn);
969
0
                    UA_UNLOCK(&el->elMutex);
970
0
                    UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf);
971
0
                    return UA_STATUSCODE_BADCONNECTIONCLOSED;
972
0
                }
973
974
                /* Poll for the socket resources to become available and retry
975
                 * (blocking) */
976
0
                int poll_ret;
977
0
                do {
978
0
                    UA_RESET_ERRNO;
979
0
                    poll_ret = UA_poll(&tmp_poll_fd, 1, 100);
980
0
                    if(poll_ret < 0 && UA_ERRNO != UA_INTERRUPTED) {
981
0
                        UA_LOG_SOCKET_ERRNO_WRAP(
982
0
                           UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
983
0
                                        "ETH %u\t| Send failed with error %s",
984
0
                                        (unsigned)connectionId, errno_str));
985
0
                        ETH_shutdown(pcm, conn);
986
0
                        UA_UNLOCK(&el->elMutex);
987
0
                        UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf);
988
0
                        return UA_STATUSCODE_BADCONNECTIONCLOSED;
989
0
                    }
990
0
                } while(poll_ret <= 0);
991
0
            }
992
0
        } while(n < 0);
993
0
        nWritten += (size_t)n;
994
0
    } while(nWritten < buf->length);
995
996
    /* Free the buffer */
997
0
    UA_UNLOCK(&el->elMutex);
998
0
    UA_EventLoopPOSIX_freeNetworkBuffer(cm, connectionId, buf);
999
0
    return UA_STATUSCODE_GOOD;
1000
0
}
1001
1002
static UA_StatusCode
1003
502
ETH_eventSourceStart(UA_ConnectionManager *cm) {
1004
502
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
1005
502
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)cm->eventSource.eventLoop;
1006
502
    UA_LOCK(&el->elMutex);
1007
1008
    /* Check the state */
1009
502
    if(cm->eventSource.state != UA_EVENTSOURCESTATE_STOPPED) {
1010
0
        UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
1011
0
                     "To start the Ethernet ConnectionManager, "
1012
0
                     "it has to be registered in an EventLoop and not started");
1013
0
        UA_UNLOCK(&el->elMutex);
1014
0
        return UA_STATUSCODE_BADINTERNALERROR;
1015
0
    }
1016
1017
    /* Check the parameters */
1018
502
    UA_StatusCode res =
1019
502
        UA_KeyValueRestriction_validate(el->eventLoop.logger, "ETH",
1020
502
                                        ethManagerParams, ETH_MANAGERPARAMS,
1021
502
                                        &cm->eventSource.params);
1022
502
    if(res != UA_STATUSCODE_GOOD)
1023
0
        goto finish;
1024
1025
    /* Allocate the rx buffer */
1026
502
    res = UA_EventLoopPOSIX_allocateStaticBuffers(pcm);
1027
502
    if(res != UA_STATUSCODE_GOOD)
1028
0
        goto finish;
1029
1030
    /* Set the EventSource to the started state */
1031
502
    cm->eventSource.state = UA_EVENTSOURCESTATE_STARTED;
1032
1033
502
 finish:
1034
502
    UA_UNLOCK(&el->elMutex);
1035
502
    return res;
1036
502
}
1037
1038
static void *
1039
0
ETH_shutdownCB(void *application, UA_RegisteredFD *rfd) {
1040
0
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)application;
1041
0
    ETH_shutdown(pcm, (ETH_FD*)rfd);
1042
0
    return NULL;
1043
0
}
1044
1045
static void
1046
502
ETH_eventSourceStop(UA_ConnectionManager *cm) {
1047
502
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
1048
502
    UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)pcm->cm.eventSource.eventLoop;
1049
502
    UA_LOCK(&el->elMutex);
1050
1051
502
    UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
1052
502
                 "ETH\t| Shutting down the ConnectionManager");
1053
1054
    /* Prevent new connections to open */
1055
502
    cm->eventSource.state = UA_EVENTSOURCESTATE_STOPPING;
1056
1057
    /* Shutdown all existing connection */
1058
502
    ZIP_ITER(UA_FDTree, &pcm->fds, ETH_shutdownCB, cm);
1059
1060
    /* Check if stopped once more (also checking inside ETH_close, but there we
1061
     * don't check if there is no rfd at all) */
1062
502
    ETH_checkStopped(pcm);
1063
1064
502
    UA_UNLOCK(&el->elMutex);
1065
502
}
1066
1067
static UA_StatusCode
1068
502
ETH_eventSourceDelete(UA_ConnectionManager *cm) {
1069
502
    UA_POSIXConnectionManager *pcm = (UA_POSIXConnectionManager*)cm;
1070
502
    if(cm->eventSource.state >= UA_EVENTSOURCESTATE_STARTING) {
1071
0
        UA_LOG_ERROR(cm->eventSource.eventLoop->logger, UA_LOGCATEGORY_EVENTLOOP,
1072
0
                     "ETH\t| The EventSource must be stopped before it can be deleted");
1073
0
        return UA_STATUSCODE_BADINTERNALERROR;
1074
0
    }
1075
1076
502
    UA_KeyValueMap_clear(&cm->eventSource.params);
1077
502
    UA_ByteString_clear(&pcm->rxBuffer);
1078
502
    UA_ByteString_clear(&pcm->txBuffer);
1079
502
    UA_String_clear(&cm->eventSource.name);
1080
502
    UA_free(cm);
1081
502
    return UA_STATUSCODE_GOOD;
1082
502
}
1083
1084
static const char *ethName = "eth";
1085
1086
UA_ConnectionManager *
1087
502
UA_ConnectionManager_new_POSIX_Ethernet(const UA_String eventSourceName) {
1088
502
    UA_POSIXConnectionManager *cm = (UA_POSIXConnectionManager*)
1089
502
        UA_calloc(1, sizeof(UA_POSIXConnectionManager));
1090
502
    if(!cm)
1091
0
        return NULL;
1092
1093
502
    cm->cm.eventSource.eventSourceType = UA_EVENTSOURCETYPE_CONNECTIONMANAGER;
1094
502
    UA_String_copy(&eventSourceName, &cm->cm.eventSource.name);
1095
502
    cm->cm.eventSource.start = (UA_StatusCode (*)(UA_EventSource *))ETH_eventSourceStart;
1096
502
    cm->cm.eventSource.stop = (void (*)(UA_EventSource *))ETH_eventSourceStop;
1097
502
    cm->cm.eventSource.free = (UA_StatusCode (*)(UA_EventSource *))ETH_eventSourceDelete;
1098
502
    cm->cm.protocol = UA_STRING((char*)(uintptr_t)ethName);
1099
502
    cm->cm.openConnection = ETH_openConnection;
1100
502
    cm->cm.allocNetworkBuffer = ETH_allocNetworkBuffer;
1101
502
    cm->cm.freeNetworkBuffer = ETH_freeNetworkBuffer;
1102
502
    cm->cm.sendWithConnection = ETH_sendWithConnection;
1103
502
    cm->cm.closeConnection = ETH_shutdownConnection;
1104
502
    return &cm->cm;
1105
502
}
1106
1107
#endif /* defined(UA_ARCHITECTURE_POSIX) && defined(__linux__) */
1108