Coverage Report

Created: 2026-10-15 06:50

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