Skip to content

Commit 80939e3

Browse files
authored
Merge pull request #274 from jow-/socket-filter-sockopt-support
socket: properly support SO_ATTACH_FILTER sockopt
2 parents 6dd0871 + 001ced1 commit 80939e3

File tree

1 file changed

+96
-7
lines changed

1 file changed

+96
-7
lines changed

Diff for: lib/socket.c

+96-7
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878

7979
#if defined(__linux__)
8080
# include <linux/if_packet.h>
81+
# include <linux/filter.h>
8182

8283
# ifndef SO_TIMESTAMP_OLD
8384
# define SO_TIMESTAMP_OLD SO_TIMESTAMP
@@ -969,6 +970,90 @@ static struct_t st_timeval = {
969970
};
970971

971972
#if defined(__linux__)
973+
static bool
974+
filter_to_c(void *st, uc_value_t *uv)
975+
{
976+
struct sock_fprog **fpp = st;
977+
struct sock_fprog *fp = *fpp;
978+
size_t i, len;
979+
980+
if (ucv_type(uv) == UC_STRING) {
981+
size_t len = ucv_string_length(uv);
982+
983+
if (len == 0 || (len % sizeof(struct sock_filter)) != 0)
984+
err_return(EINVAL, "Filter program length not a multiple of %zu",
985+
sizeof(struct sock_filter));
986+
987+
fp = *fpp = xrealloc(fp, sizeof(struct sock_fprog) + len);
988+
fp->filter = memcpy((char *)fp + sizeof(struct sock_fprog), ucv_string_get(uv), len);
989+
990+
if (fp->len == 0)
991+
fp->len = len / sizeof(struct sock_filter);
992+
}
993+
else if (ucv_type(uv) == UC_ARRAY) {
994+
/* Opcode array of array. Each sub-array is a 4 element tuple */
995+
if (ucv_type(ucv_array_get(uv, 0)) == UC_ARRAY) {
996+
len = ucv_array_length(uv);
997+
998+
fp = *fpp = xrealloc(fp, sizeof(struct sock_fprog)
999+
+ (len * sizeof(struct sock_filter)));
1000+
1001+
fp->filter = (struct sock_filter *)((char *)fp + sizeof(struct sock_fprog));
1002+
1003+
for (i = 0; i < len; i++) {
1004+
uc_value_t *op = ucv_array_get(uv, i);
1005+
1006+
if (ucv_type(op) != UC_ARRAY)
1007+
continue;
1008+
1009+
fp->filter[i].code = ucv_to_unsigned(ucv_array_get(op, 0));
1010+
fp->filter[i].jt = ucv_to_unsigned(ucv_array_get(op, 1));
1011+
fp->filter[i].jf = ucv_to_unsigned(ucv_array_get(op, 2));
1012+
fp->filter[i].k = ucv_to_unsigned(ucv_array_get(op, 3));
1013+
}
1014+
}
1015+
1016+
/* Flat opcode array, must be a multiple of 4 */
1017+
else {
1018+
len = ucv_array_length(uv);
1019+
1020+
if (len % 4)
1021+
err_return(EINVAL, "Opcode array length not a multiple of 4");
1022+
1023+
len /= 4;
1024+
1025+
fp = *fpp = xrealloc(fp, sizeof(struct sock_fprog)
1026+
+ (len * sizeof(struct sock_filter)));
1027+
1028+
fp->filter = (struct sock_filter *)((char *)fp + sizeof(struct sock_fprog));
1029+
1030+
for (i = 0; i < len; i++) {
1031+
fp->filter[i].code = ucv_to_unsigned(ucv_array_get(uv, i * 4 + 0));
1032+
fp->filter[i].jt = ucv_to_unsigned(ucv_array_get(uv, i * 4 + 1));
1033+
fp->filter[i].jf = ucv_to_unsigned(ucv_array_get(uv, i * 4 + 2));
1034+
fp->filter[i].k = ucv_to_unsigned(ucv_array_get(uv, i * 4 + 3));
1035+
}
1036+
}
1037+
1038+
if (fp->len == 0)
1039+
fp->len = i;
1040+
}
1041+
else {
1042+
err_return(EINVAL, "Expecting either BPF bytecode string or array of opcodes");
1043+
}
1044+
1045+
return true;
1046+
}
1047+
1048+
static struct_t st_sock_fprog = {
1049+
.size = sizeof(struct sock_fprog),
1050+
.members = (member_t []){
1051+
STRUCT_MEMBER_NP(sock_fprog, len, DT_UNSIGNED),
1052+
STRUCT_MEMBER_CB(filter, filter_to_c, NULL),
1053+
{ 0 }
1054+
}
1055+
};
1056+
9721057
static struct_t st_ucred = {
9731058
.size = sizeof(struct ucred),
9741059
.members = (member_t []){
@@ -1051,7 +1136,7 @@ in6_ifindex_to_uv(void *st)
10511136
static bool
10521137
in6_ifindex_to_c(void *st, uc_value_t *uv)
10531138
{
1054-
struct ipv6_mreq *mr = st;
1139+
struct ipv6_mreq *mr = *(struct ipv6_mreq **)st;
10551140

10561141
if (ucv_type(uv) == UC_STRING) {
10571142
mr->ipv6mr_interface = if_nametoindex(ucv_string_get(uv));
@@ -1189,7 +1274,9 @@ rcv_wscale_to_uv(void *st)
11891274
static bool
11901275
snd_wscale_to_c(void *st, uc_value_t *uv)
11911276
{
1192-
((struct tcp_info *)st)->tcpi_snd_wscale = ucv_to_unsigned(uv);
1277+
struct tcp_info *ti = *(struct tcp_info **)st;
1278+
1279+
ti->tcpi_snd_wscale = ucv_to_unsigned(uv);
11931280

11941281
if (errno)
11951282
err_return(errno, "Unable to convert field snd_wscale to unsigned");
@@ -1200,7 +1287,9 @@ snd_wscale_to_c(void *st, uc_value_t *uv)
12001287
static bool
12011288
rcv_wscale_to_c(void *st, uc_value_t *uv)
12021289
{
1203-
((struct tcp_info *)st)->tcpi_rcv_wscale = ucv_to_unsigned(uv);
1290+
struct tcp_info *ti = *(struct tcp_info **)st;
1291+
1292+
ti->tcpi_rcv_wscale = ucv_to_unsigned(uv);
12041293

12051294
if (errno)
12061295
err_return(errno, "Unable to convert field rcv_wscale to unsigned");
@@ -1314,7 +1403,7 @@ mr_ifindex_to_uv(void *st)
13141403
static bool
13151404
mr_ifindex_to_c(void *st, uc_value_t *uv)
13161405
{
1317-
struct packet_mreq *mr = st;
1406+
struct packet_mreq *mr = *(struct packet_mreq **)st;
13181407

13191408
if (ucv_type(uv) == UC_STRING) {
13201409
mr->mr_ifindex = if_nametoindex(ucv_string_get(uv));
@@ -1344,7 +1433,7 @@ mr_address_to_uv(void *st)
13441433
static bool
13451434
mr_address_to_c(void *st, uc_value_t *uv)
13461435
{
1347-
struct packet_mreq *mr = st;
1436+
struct packet_mreq *mr = *(struct packet_mreq **)st;
13481437
size_t len;
13491438

13501439
if (!uv_to_hwaddr(uv, mr->mr_address, &len))
@@ -1507,7 +1596,7 @@ static sockopt_t sockopts[] = {
15071596
{ SOL_SOCKET, SO_TIMESTAMP, SV_BOOL },
15081597
{ SOL_SOCKET, SO_TYPE, SV_INT },
15091598
#if defined(__linux__)
1510-
{ SOL_SOCKET, SO_ATTACH_FILTER, SV_STRING },
1599+
{ SOL_SOCKET, SO_ATTACH_FILTER, &st_sock_fprog },
15111600
{ SOL_SOCKET, SO_ATTACH_BPF, SV_INT },
15121601
{ SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, SV_STRING },
15131602
{ SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, SV_INT },
@@ -1792,7 +1881,7 @@ uv_to_struct(uc_value_t *uv, struct_t *spec)
17921881
break;
17931882

17941883
case DT_CALLBACK:
1795-
if (m->u1.to_c && !m->u1.to_c(st, fv)) {
1884+
if (m->u1.to_c && !m->u1.to_c(&st, fv)) {
17961885
free(st);
17971886
return NULL;
17981887
}

0 commit comments

Comments
 (0)