16static size_t put16(uint8_t *p, uint16_t v)
18 p[0] = (uint8_t)(v & 0xFF);
19 p[1] = (uint8_t)(v >> 8);
23static size_t put32(uint8_t *p, uint32_t v)
25 p[0] = (uint8_t)(v & 0xFF);
26 p[1] = (uint8_t)(v >> 8);
27 p[2] = (uint8_t)(v >> 16);
28 p[3] = (uint8_t)(v >> 24);
32static uint16_t get16(
const uint8_t *p)
34 return (uint16_t)(p[0] | ((uint16_t)p[1] << 8));
37static uint32_t get32(
const uint8_t *p)
39 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
42size_t eip_build(uint8_t *buf,
size_t cap,
const EipHeader *h,
const uint8_t *data,
size_t data_len)
44 if (!buf || !h || (data_len && !data) || data_len > 0xFFFF)
46 size_t total = EIP_HEADER_SIZE + data_len;
50 p += put16(buf + p, h->command);
51 p += put16(buf + p, (uint16_t)data_len);
52 p += put32(buf + p, h->session_handle);
53 p += put32(buf + p, h->status);
54 memcpy(buf + p, h->sender_context, 8);
56 p += put32(buf + p, h->options);
59 memcpy(buf + p, data, data_len);
65bool eip_parse(
const uint8_t *buf,
size_t len, EipHeader *out,
const uint8_t **data,
size_t *data_len)
67 if (!buf || !out || len < EIP_HEADER_SIZE)
69 out->command = get16(buf);
70 out->length = get16(buf + 2);
71 out->session_handle = get32(buf + 4);
72 out->status = get32(buf + 8);
73 memcpy(out->sender_context, buf + 12, 8);
74 out->options = get32(buf + 20);
75 if ((
size_t)EIP_HEADER_SIZE + out->length > len)
78 *data = buf + EIP_HEADER_SIZE;
80 *data_len = out->length;
84size_t eip_build_register_session(uint8_t *buf,
size_t cap,
const uint8_t sender_context[8])
87 memset(&h, 0,
sizeof(h));
88 h.command = EIP_CMD_REGISTER_SESSION;
90 memcpy(h.sender_context, sender_context, 8);
94 return eip_build(buf, cap, &h, data,
sizeof(data));
97size_t eip_build_send_rr_data(uint8_t *buf,
size_t cap, uint32_t session_handle,
const uint8_t sender_context[8],
98 uint16_t timeout,
const uint8_t *cip,
size_t cip_len)
100 if (!buf || (cip_len && !cip) || cip_len > 0xFFFF)
103 size_t data_len = 4 + 2 + 2 + 4 + 4 + cip_len;
104 size_t total = EIP_HEADER_SIZE + data_len;
105 if (total > cap || data_len > 0xFFFF)
111 memset(&h, 0,
sizeof(h));
112 h.command = EIP_CMD_SEND_RR_DATA;
113 h.session_handle = session_handle;
115 memcpy(h.sender_context, sender_context, 8);
116 if (eip_build(buf, cap, &h,
nullptr, 0) == 0)
120 put16(buf + 2, (uint16_t)data_len);
122 size_t p = EIP_HEADER_SIZE;
123 p += put32(buf + p, 0);
124 p += put16(buf + p, timeout);
125 p += put16(buf + p, 2);
126 p += put16(buf + p, EIP_CPF_NULL);
127 p += put16(buf + p, 0);
128 p += put16(buf + p, EIP_CPF_UNCONNECTED_DATA);
129 p += put16(buf + p, (uint16_t)cip_len);
132 memcpy(buf + p, cip, cip_len);
138bool eip_parse_send_rr_data(
const uint8_t *data,
size_t data_len,
const uint8_t **cip,
size_t *cip_len)
140 if (!data || data_len < 8)
143 uint16_t item_count = get16(data + pos);
145 for (uint16_t i = 0; i < item_count; i++)
147 if (pos + 4 > data_len)
149 uint16_t type = get16(data + pos);
150 uint16_t ilen = get16(data + pos + 2);
152 if (pos + ilen > data_len)
154 if (type == EIP_CPF_UNCONNECTED_DATA)
EtherNet/IP encapsulation codec (DETWS_ENABLE_ENIP) - zero-heap builder + parser for the ODVA EtherNe...