18size_t len_octets(
size_t len)
25 for (
size_t v = len; v; v >>= 8)
33size_t write_len(uint8_t *p,
size_t len)
41 for (
size_t v = len; v; v >>= 8)
45 p[0] = (uint8_t)(0x80 | nb);
46 for (
size_t i = 0; i < nb; i++)
48 p[1 + i] = (uint8_t)(len >> (8 * (nb - 1 - i)));
54bool tlv(uint8_t *out,
size_t cap,
size_t *n, uint8_t tag,
const uint8_t *val,
size_t val_len)
56 size_t need = 1 + len_octets(val_len) + val_len;
62 *n += write_len(out + *n, val_len);
65 memcpy(out + *n, val, val_len);
72bool pc_ber_int(uint8_t *out,
size_t cap,
size_t *n, uint8_t tag, uint32_t v)
85 for (uint32_t x = v; x; x >>= 8)
87 tmp[t++] = (uint8_t)x;
89 if (tmp[t - 1] & 0x80)
93 for (
size_t i = 0; i < t; i++)
95 buf[k++] = tmp[t - 1 - i];
98 return tlv(out, cap, n, tag, buf, k);
101bool pc_ber_str(uint8_t *out,
size_t cap,
size_t *n, uint8_t tag,
const char *s)
103 return tlv(out, cap, n, tag, (
const uint8_t *)(s ? s :
""), s ? strnlen(s, cap + 1) : 0);
106bool pc_ber_bool(uint8_t *out,
size_t cap,
size_t *n, uint8_t tag,
bool v)
108 uint8_t b = v ? 0xFF : 0x00;
109 return tlv(out, cap, n, tag, &b, 1);
114bool ber_read(
const uint8_t *end,
const uint8_t **p, uint8_t *tag,
const uint8_t **vptr,
size_t *vlen)
116 const uint8_t *q = *p;
125 size_t nb = len & 0x7F;
126 if (nb == 0 || nb > 4 || q + nb > end)
131 for (
size_t i = 0; i < nb; i++)
133 len = (len << 8) | *q++;
136 if (len > (
size_t)(end - q))
148uint32_t ber_uint(
const uint8_t *v,
size_t n)
151 for (
size_t i = 0; i < n; i++)
159size_t pc_goose_pdu(
const pc_goose *g, uint8_t *out,
size_t cap)
165 const size_t RESERVE = 4;
172 static const uint8_t ZT[8] = {0};
173 bool ok = pc_ber_str(out, cap, &n, 0x80, g->gocb_ref) && pc_ber_int(out, cap, &n, 0x81, g->time_allowed_to_live) &&
174 pc_ber_str(out, cap, &n, 0x82, g->dat_set) && pc_ber_str(out, cap, &n, 0x83, g->go_id) &&
175 tlv(out, cap, &n, 0x84, g->t ? g->t : ZT, 8) && pc_ber_int(out, cap, &n, 0x85, g->st_num) &&
176 pc_ber_int(out, cap, &n, 0x86, g->sq_num) && pc_ber_bool(out, cap, &n, 0x87, g->simulation) &&
177 pc_ber_int(out, cap, &n, 0x88, g->conf_rev) && pc_ber_bool(out, cap, &n, 0x89, g->nds_com) &&
178 pc_ber_int(out, cap, &n, 0x8A, g->num_entries) && tlv(out, cap, &n, 0xAB, g->all_data, g->all_data_len);
184 size_t content_len = n - RESERVE;
185 size_t hdr = 1 + len_octets(content_len);
187 memmove(out + hdr, out + RESERVE, content_len);
189 write_len(out + 1, content_len);
190 return hdr + content_len;
193size_t pc_goose_frame(
const uint8_t *dst,
const uint8_t *src, uint16_t appid,
const pc_goose *g, uint8_t *out,
196 if (!dst || !src || !g || !out || cap < 22)
202 memcpy(out + 6, src, 6);
206 out[14] = (uint8_t)(appid >> 8);
207 out[15] = (uint8_t)appid;
213 size_t pdu = pc_goose_pdu(g, out + 22, cap - 22);
218 uint16_t goose_len = (uint16_t)(8 + pdu);
219 out[16] = (uint8_t)(goose_len >> 8);
220 out[17] = (uint8_t)goose_len;
224bool pc_goose_parse_frame(
const uint8_t *buf,
size_t len, pc_goose_rx *out)
226 if (!buf || !out || len < 24)
230 if (buf[12] != 0x88 || buf[13] != 0xB8)
234 memset(out, 0,
sizeof(*out));
235 out->appid = (uint16_t)((buf[14] << 8) | buf[15]);
237 const uint8_t *end = buf + len;
238 const uint8_t *p = buf + 22;
240 const uint8_t *v =
nullptr;
242 if (!ber_read(end, &p, &tag, &v, &vlen) || tag != 0x61)
247 const uint8_t *inner_end = v + vlen;
248 const uint8_t *ip = v;
249 while (ip < inner_end)
251 if (!ber_read(inner_end, &ip, &tag, &v, &vlen))
258 out->gocb_ref = (
const char *)v;
259 out->gocb_ref_len = vlen;
262 out->time_allowed_to_live = ber_uint(v, vlen);
265 out->dat_set = (
const char *)v;
266 out->dat_set_len = vlen;
269 out->go_id = (
const char *)v;
270 out->go_id_len = vlen;
279 out->st_num = ber_uint(v, vlen);
282 out->sq_num = ber_uint(v, vlen);
285 out->simulation = (vlen >= 1 && v[0] != 0);
288 out->conf_rev = ber_uint(v, vlen);
291 out->nds_com = (vlen >= 1 && v[0] != 0);
294 out->num_entries = ber_uint(v, vlen);
298 out->all_data_len = vlen;
IEC 61850 GOOSE publisher + subscriber codec (PC_ENABLE_GOOSE).