diff --git a/README.md b/README.md index 68dd1d9..29a4486 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ General Options: -s enable silent mode -w write log to instead of stderr +Port Filters: + -p comma-separated whitelist ports eq 53,80-1000 + -P comma-separated blacklist ports eq 51820,51413 + Advanced Options: -f skip firewall rules -g disable hop count estimation @@ -45,7 +49,6 @@ Advanced Options: -x set the mask for fwmark -y raise TTL dynamically to % of estimated hops -z use iptables commands instead of nft - ``` diff --git a/include/globvar.h b/include/globvar.h index 63c7e07..e504076 100644 --- a/include/globvar.h +++ b/include/globvar.h @@ -28,7 +28,7 @@ struct fs_context { int exit; FILE *logfp; - /* -b, -e, -h */ struct payload_info *plinfo; + /* -b, -u */ struct payload_info *plinfo; /* -0 */ int inbound; /* -1 */ int outbound; /* -4 */ int use_ipv4; @@ -48,6 +48,8 @@ struct fs_context { /* -x */ uint32_t fwmask; /* -y */ int dynamic_pct; /* -z */ int use_iptables; + /* -p */ uint8_t *port_white; + /* -P */ uint8_t *port_black; }; extern struct fs_context g_ctx; diff --git a/include/portmap.h b/include/portmap.h new file mode 100644 index 0000000..e27b2fe --- /dev/null +++ b/include/portmap.h @@ -0,0 +1,31 @@ +/* + * portmap.h - FakeSIP: https://github.com/MikeWang000000/FakeSIP + * + * Copyright (C) 2025 MikeWang000000 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef FS_PORTMAP_H +#define FS_PORTMAP_H + +#include + +int fs_portmap_get(const uint8_t *bitmap, uint16_t port); + +void fs_portmap_set(uint8_t *bitmap, uint16_t port); + +int fs_portmap_parse(uint8_t **bitmap_ptr, const char *spec); + +#endif /* FS_PORTMAP_H */ diff --git a/src/globvar.c b/src/globvar.c index 4abeada..7ebe786 100644 --- a/src/globvar.c +++ b/src/globvar.c @@ -45,4 +45,6 @@ struct fs_context g_ctx = {.exit = 0, /* -w */ .logpath = NULL, /* -x */ .fwmask = 0, /* -y */ .dynamic_pct = 0, - /* -z */ .use_iptables = 0}; + /* -z */ .use_iptables = 0, + /* -p */ .port_white = NULL, + /* -P */ .port_black = NULL}; diff --git a/src/ipv6ipt.c b/src/ipv6ipt.c index 7e6027f..aa3c4a3 100644 --- a/src/ipv6ipt.c +++ b/src/ipv6ipt.c @@ -107,8 +107,8 @@ int fs_ipt6_setup(void) /* drop time-exceeded ICMP packets */ - {"iptables", "-w", "-t", "mangle", "-A", "FAKESIP_S", "-p", "icmp", - "--icmp-type", "11", "-j", "DROP", NULL}, + {"ip6tables", "-w", "-t", "mangle", "-A", "FAKESIP_S", "-p", "icmpv6", + "--icmpv6-type", "3", "-j", "DROP", NULL}, /* exclude non-GUA IPv6 addresses (from source) diff --git a/src/mainfun.c b/src/mainfun.c index 9bc9a7a..81020d6 100644 --- a/src/mainfun.c +++ b/src/mainfun.c @@ -41,6 +41,7 @@ #include "rawsend.h" #include "signals.h" #include "srcinfo.h" +#include "portmap.h" #ifndef PROGNAME #define PROGNAME "fakesip" @@ -73,6 +74,10 @@ static void print_usage(const char *name) " -s enable silent mode\n" " -w write log to instead of stderr\n" "\n" + "Port Filters:\n" + " -p comma-separated whitelist ports eq 53,80-1000\n" + " -P comma-separated blacklist ports eq 51820,51413\n" + "\n" "Advanced Options:\n" " -f skip firewall rules\n" " -g disable hop count estimation\n" @@ -124,7 +129,7 @@ int main(int argc, char *argv[]) plinfo_cnt = iface_cnt = 0; - while ((opt = getopt(argc, argv, "0146ab:dfgi:km:n:r:st:u:w:x:y:z")) != + while ((opt = getopt(argc, argv, "0146ab:dfgi:km:n:r:st:u:w:x:y:zp:P:")) != -1) { switch (opt) { case '0': @@ -278,6 +283,30 @@ int main(int argc, char *argv[]) } break; + case 'p': + case 'P': { + if (!optarg[0]) { + fprintf(stderr, "%s: value of -%c cannot be empty.\n", + argv[0], opt); + print_usage(argv[0]); + goto free_mem; + } + + if (opt == 'p') { + if (fs_portmap_parse(&g_ctx.port_white, optarg) < 0) { + fprintf(stderr, "%s: invalid port spec for -p: %s\n", + argv[0], optarg); + goto free_mem; + } + } else { + if (fs_portmap_parse(&g_ctx.port_black, optarg) < 0) { + fprintf(stderr, "%s: invalid port spec for -P: %s\n", + argv[0], optarg); + goto free_mem; + } + } + } break; + case 'x': tmp = strtoull(optarg, NULL, 0); if (!tmp || tmp > UINT32_MAX) { @@ -490,5 +519,13 @@ int main(int argc, char *argv[]) free(g_ctx.iface); } + if (g_ctx.port_white) { + free(g_ctx.port_white); + } + + if (g_ctx.port_black) { + free(g_ctx.port_black); + } + return exitcode; } diff --git a/src/portmap.c b/src/portmap.c new file mode 100644 index 0000000..5d2ca2d --- /dev/null +++ b/src/portmap.c @@ -0,0 +1,99 @@ +/* + * portmap.c - FakeSIP: https://github.com/MikeWang000000/FakeSIP + * + * Copyright (C) 2025 MikeWang000000 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _GNU_SOURCE +#include "portmap.h" + +#include +#include +#include +#include + +int fs_portmap_parse(uint8_t **bitmap_ptr, const char *spec) +{ + char *sdup = NULL, *tok, *saveptr; + int ret = -1; + + if (!spec || !spec[0]) { + return -1; + } + + sdup = strdup(spec); + if (!sdup) + return -1; + + for (tok = strtok_r(sdup, ",", &saveptr); tok; + tok = strtok_r(NULL, ",", &saveptr)) { + char *dash = strchr(tok, '-'); + unsigned long start, end; + char *endptr; + + if (!dash) { + start = strtoul(tok, &endptr, 0); + if (endptr == tok || *endptr != '\0' || start == 0 || + start > 65535) { + goto out; + } + end = start; + } else { + *dash = '\0'; + start = strtoul(tok, &endptr, 0); + if (endptr == tok || *endptr != '\0' || start == 0 || + start > 65535) { + goto out; + } + end = strtoul(dash + 1, &endptr, 0); + if (endptr == dash + 1 || *endptr != '\0' || end == 0 || + end > 65535) { + goto out; + } + if (start > end) + goto out; + } + + if (!*bitmap_ptr) { + *bitmap_ptr = calloc(1, 8192); + if (!*bitmap_ptr) + goto out; + } + + for (unsigned long p = start; p <= end; p++) { + fs_portmap_set(*bitmap_ptr, (uint16_t) p); + } + } + + ret = 0; +out: + free(sdup); + return ret; +} + +int fs_portmap_get(const uint8_t *bitmap, uint16_t port) +{ + if (!bitmap) + return 0; + return (bitmap[port >> 3] & (uint8_t) (1u << (port & 7))) ? 1 : 0; +} + +void fs_portmap_set(uint8_t *bitmap, uint16_t port) +{ + if (!bitmap) + return; + bitmap[port >> 3] |= (uint8_t) (1u << (port & 7)); +} diff --git a/src/rawsend.c b/src/rawsend.c index 5bcb0ef..dc8e858 100644 --- a/src/rawsend.c +++ b/src/rawsend.c @@ -39,6 +39,7 @@ #include "logging.h" #include "payload.h" #include "srcinfo.h" +#include "portmap.h" #define NO_SNAT 0 #define NEED_SNAT 1 @@ -375,6 +376,36 @@ int fs_rawsend_handle(struct sockaddr_ll *sll, uint8_t *pkt_data, int pkt_len, ipaddr_to_str(daddr, dst_ip_str); } + /* Port whitelist/blacklist filtering: if whitelist is set, only process + packets where either source or dest port is in the whitelist. If any + port matches the blacklist, skip processing. */ + { + uint16_t sport = ntohs(udph->source); + uint16_t dport = ntohs(udph->dest); + + if (g_ctx.port_white) { + if (!(fs_portmap_get(g_ctx.port_white, sport) || + fs_portmap_get(g_ctx.port_white, dport))) { + if (!g_ctx.silent) { + E_INFO("%s:%u ===(SKIP)=== %s:%u", src_ip_str, sport, + dst_ip_str, dport); + } + return NF_ACCEPT; + } + } + + if (g_ctx.port_black) { + if (fs_portmap_get(g_ctx.port_black, sport) || + fs_portmap_get(g_ctx.port_black, dport)) { + if (!g_ctx.silent) { + E_INFO("%s:%u ===(SKIP)=== %s:%u", src_ip_str, sport, + dst_ip_str, dport); + } + return NF_ACCEPT; + } + } + } + if (sll->sll_pkttype == PACKET_HOST) { /* Inbound UDP packet.