diff options
-rw-r--r-- | .github/workflows/debian.yml | 2 | ||||
-rw-r--r-- | .github/workflows/jsdoc.yml | 2 | ||||
-rw-r--r-- | .github/workflows/macos.yml | 2 | ||||
-rw-r--r-- | .github/workflows/openwrt-ci-master.yml | 2 | ||||
-rw-r--r-- | .github/workflows/openwrt-ci-pull-request.yml | 2 | ||||
-rw-r--r-- | docs/README.md | 6 | ||||
-rw-r--r-- | lib/rtnl.c | 10 | ||||
-rw-r--r-- | lib/socket.c | 114 | ||||
-rw-r--r-- | main.c | 4 |
9 files changed, 118 insertions, 26 deletions
diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index 52c6f9e..6286185 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -29,7 +29,7 @@ jobs: dpkg-buildpackage -b -us -uc - name: Archive code coverage results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: deb path: '*ucode*.deb' diff --git a/.github/workflows/jsdoc.yml b/.github/workflows/jsdoc.yml index 9d9d394..427250b 100644 --- a/.github/workflows/jsdoc.yml +++ b/.github/workflows/jsdoc.yml @@ -20,7 +20,7 @@ jobs: run: npm run doc - name: Archive docs as artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docs path: ./docs/ diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 72f3454..f8eb43b 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -31,7 +31,7 @@ jobs: make - name: Upload build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: minimal-build diff --git a/.github/workflows/openwrt-ci-master.yml b/.github/workflows/openwrt-ci-master.yml index f95c924..53ded7c 100644 --- a/.github/workflows/openwrt-ci-master.yml +++ b/.github/workflows/openwrt-ci-master.yml @@ -24,7 +24,7 @@ jobs: - uses: ynezz/gh-actions-openwrt-ci-native@v0.0.2 - name: Upload build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: native-build-artifacts diff --git a/.github/workflows/openwrt-ci-pull-request.yml b/.github/workflows/openwrt-ci-pull-request.yml index 8d6ad34..4db2cb8 100644 --- a/.github/workflows/openwrt-ci-pull-request.yml +++ b/.github/workflows/openwrt-ci-pull-request.yml @@ -26,7 +26,7 @@ jobs: CI_CLANG_VERSION_LIST: 11 - name: Upload build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: native-build-artifacts diff --git a/docs/README.md b/docs/README.md index 016c7f5..92ab103 100644 --- a/docs/README.md +++ b/docs/README.md @@ -69,14 +69,14 @@ command. ### MacOS -To build on MacOS, first install *cmake* and *json-c* via +To build on MacOS, first install *cmake*, *json-c* and *libmd* via [Homebrew](https://brew.sh/), then clone the ucode repository and execute *cmake* followed by *make*: - $ brew install cmake json-c + $ brew install cmake json-c libmd $ git clone https://github.com/jow-/ucode.git $ cd ucode/ - $ cmake -DUBUS_SUPPORT=OFF -DUCI_SUPPORT=OFF -DULOOP_SUPPORT=OFF . + $ cmake -DUBUS_SUPPORT=OFF -DUCI_SUPPORT=OFF -DULOOP_SUPPORT=OFF -DCMAKE_BUILD_RPATH=/usr/local/lib -DCMAKE_INSTALL_RPATH=/usr/local/lib . $ make $ sudo make install @@ -3612,6 +3612,10 @@ cb_listener_event(struct nl_msg *msg, void *arg) if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) { uloop_end(); + set_error(NLE_FAILURE, "Runtime exception in callback"); + + errno = EINVAL; + return NL_STOP; } @@ -3631,12 +3635,8 @@ uc_nl_listener_cb(struct uloop_fd *fd, unsigned int events) nl_recvmsgs_default(nl_conn.evsock); - if (errno != 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) - set_error(errno, NULL); - + if (errno != 0) break; - } } } diff --git a/lib/socket.c b/lib/socket.c index db77188..f4b10de 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -78,6 +78,7 @@ #if defined(__linux__) # include <linux/if_packet.h> +# include <linux/filter.h> # ifndef SO_TIMESTAMP_OLD # define SO_TIMESTAMP_OLD SO_TIMESTAMP @@ -306,13 +307,16 @@ hwaddr_to_uv(uint8_t *addr, size_t alen) char buf[sizeof("FF:FF:FF:FF:FF:FF:FF:FF")], *p = buf; const char *hex = "0123456789ABCDEF"; - for (size_t i = 0; i < alen && i < 8; i++) { + if (alen > 8) + alen = 8; + + for (size_t i = 0; i < alen; i++) { if (i) *p++ = ':'; *p++ = hex[addr[i] / 16]; *p++ = hex[addr[i] % 16]; } - return ucv_string_new(buf); + return ucv_string_new_length(buf, alen); } static bool @@ -966,6 +970,90 @@ static struct_t st_timeval = { }; #if defined(__linux__) +static bool +filter_to_c(void *st, uc_value_t *uv) +{ + struct sock_fprog **fpp = st; + struct sock_fprog *fp = *fpp; + size_t i, len; + + if (ucv_type(uv) == UC_STRING) { + size_t len = ucv_string_length(uv); + + if (len == 0 || (len % sizeof(struct sock_filter)) != 0) + err_return(EINVAL, "Filter program length not a multiple of %zu", + sizeof(struct sock_filter)); + + fp = *fpp = xrealloc(fp, sizeof(struct sock_fprog) + len); + fp->filter = memcpy((char *)fp + sizeof(struct sock_fprog), ucv_string_get(uv), len); + + if (fp->len == 0) + fp->len = len / sizeof(struct sock_filter); + } + else if (ucv_type(uv) == UC_ARRAY) { + /* Opcode array of array. Each sub-array is a 4 element tuple */ + if (ucv_type(ucv_array_get(uv, 0)) == UC_ARRAY) { + len = ucv_array_length(uv); + + fp = *fpp = xrealloc(fp, sizeof(struct sock_fprog) + + (len * sizeof(struct sock_filter))); + + fp->filter = (struct sock_filter *)((char *)fp + sizeof(struct sock_fprog)); + + for (i = 0; i < len; i++) { + uc_value_t *op = ucv_array_get(uv, i); + + if (ucv_type(op) != UC_ARRAY) + continue; + + fp->filter[i].code = ucv_to_unsigned(ucv_array_get(op, 0)); + fp->filter[i].jt = ucv_to_unsigned(ucv_array_get(op, 1)); + fp->filter[i].jf = ucv_to_unsigned(ucv_array_get(op, 2)); + fp->filter[i].k = ucv_to_unsigned(ucv_array_get(op, 3)); + } + } + + /* Flat opcode array, must be a multiple of 4 */ + else { + len = ucv_array_length(uv); + + if (len % 4) + err_return(EINVAL, "Opcode array length not a multiple of 4"); + + len /= 4; + + fp = *fpp = xrealloc(fp, sizeof(struct sock_fprog) + + (len * sizeof(struct sock_filter))); + + fp->filter = (struct sock_filter *)((char *)fp + sizeof(struct sock_fprog)); + + for (i = 0; i < len; i++) { + fp->filter[i].code = ucv_to_unsigned(ucv_array_get(uv, i * 4 + 0)); + fp->filter[i].jt = ucv_to_unsigned(ucv_array_get(uv, i * 4 + 1)); + fp->filter[i].jf = ucv_to_unsigned(ucv_array_get(uv, i * 4 + 2)); + fp->filter[i].k = ucv_to_unsigned(ucv_array_get(uv, i * 4 + 3)); + } + } + + if (fp->len == 0) + fp->len = i; + } + else { + err_return(EINVAL, "Expecting either BPF bytecode string or array of opcodes"); + } + + return true; +} + +static struct_t st_sock_fprog = { + .size = sizeof(struct sock_fprog), + .members = (member_t []){ + STRUCT_MEMBER_NP(sock_fprog, len, DT_UNSIGNED), + STRUCT_MEMBER_CB(filter, filter_to_c, NULL), + { 0 } + } +}; + static struct_t st_ucred = { .size = sizeof(struct ucred), .members = (member_t []){ @@ -1048,7 +1136,7 @@ in6_ifindex_to_uv(void *st) static bool in6_ifindex_to_c(void *st, uc_value_t *uv) { - struct ipv6_mreq *mr = st; + struct ipv6_mreq *mr = *(struct ipv6_mreq **)st; if (ucv_type(uv) == UC_STRING) { mr->ipv6mr_interface = if_nametoindex(ucv_string_get(uv)); @@ -1186,7 +1274,9 @@ rcv_wscale_to_uv(void *st) static bool snd_wscale_to_c(void *st, uc_value_t *uv) { - ((struct tcp_info *)st)->tcpi_snd_wscale = ucv_to_unsigned(uv); + struct tcp_info *ti = *(struct tcp_info **)st; + + ti->tcpi_snd_wscale = ucv_to_unsigned(uv); if (errno) err_return(errno, "Unable to convert field snd_wscale to unsigned"); @@ -1197,7 +1287,9 @@ snd_wscale_to_c(void *st, uc_value_t *uv) static bool rcv_wscale_to_c(void *st, uc_value_t *uv) { - ((struct tcp_info *)st)->tcpi_rcv_wscale = ucv_to_unsigned(uv); + struct tcp_info *ti = *(struct tcp_info **)st; + + ti->tcpi_rcv_wscale = ucv_to_unsigned(uv); if (errno) err_return(errno, "Unable to convert field rcv_wscale to unsigned"); @@ -1311,7 +1403,7 @@ mr_ifindex_to_uv(void *st) static bool mr_ifindex_to_c(void *st, uc_value_t *uv) { - struct packet_mreq *mr = st; + struct packet_mreq *mr = *(struct packet_mreq **)st; if (ucv_type(uv) == UC_STRING) { mr->mr_ifindex = if_nametoindex(ucv_string_get(uv)); @@ -1341,7 +1433,7 @@ mr_address_to_uv(void *st) static bool mr_address_to_c(void *st, uc_value_t *uv) { - struct packet_mreq *mr = st; + struct packet_mreq *mr = *(struct packet_mreq **)st; size_t len; if (!uv_to_hwaddr(uv, mr->mr_address, &len)) @@ -1504,7 +1596,7 @@ static sockopt_t sockopts[] = { { SOL_SOCKET, SO_TIMESTAMP, SV_BOOL }, { SOL_SOCKET, SO_TYPE, SV_INT }, #if defined(__linux__) - { SOL_SOCKET, SO_ATTACH_FILTER, SV_STRING }, + { SOL_SOCKET, SO_ATTACH_FILTER, &st_sock_fprog }, { SOL_SOCKET, SO_ATTACH_BPF, SV_INT }, { SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, SV_STRING }, { SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, SV_INT }, @@ -1789,7 +1881,7 @@ uv_to_struct(uc_value_t *uv, struct_t *spec) break; case DT_CALLBACK: - if (m->u1.to_c && !m->u1.to_c(st, fv)) { + if (m->u1.to_c && !m->u1.to_c(&st, fv)) { free(st); return NULL; } @@ -3920,10 +4012,6 @@ uc_socket_inst_recvmsg(uc_vm_t *vm, size_t nargs) flagval = flags ? ucv_int64_get(flags) : 0; -#if defined(__linux__) - flagval |= MSG_CMSG_CLOEXEC; -#endif - /* prepare ancillary data buffer */ if (anclength) { size_t sz = ucv_to_unsigned(anclength); @@ -506,6 +506,7 @@ main(int argc, char **argv) FILE *precompile = NULL; char *outfile = NULL; uc_vm_t vm = { 0 }; + const char *argv0; int opt, rv = 0; const char *app; uc_value_t *o; @@ -558,6 +559,7 @@ main(int argc, char **argv) uc_search_path_init(&config.module_search_path); + argv0 = argv[optind]; optind = 1; uc_vm_init(&vm, &config); @@ -569,6 +571,8 @@ main(int argc, char **argv) o = ucv_array_new(&vm); ucv_object_add(uc_vm_scope_get(&vm), "ARGV", ucv_get(o)); + if (argv0) + ucv_object_add(uc_vm_scope_get(&vm), "SCRIPT_NAME", ucv_string_new(argv0)); /* parse options iteration 2: process remaining options */ while ((opt = getopt(argc, argv, optspec)) != -1) |