summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bpf/filter/bpf.c82
-rw-r--r--bpf/filter/bpf_bpfel.go119
-rw-r--r--bpf/filter/bpf_bpfel.obin0 -> 3728 bytes
-rw-r--r--bpf/filter/gen.go3
-rw-r--r--bpf/filter/main.go95
-rw-r--r--cmd/main.go6
-rw-r--r--db/models.go4
-rw-r--r--db/query.sql14
-rw-r--r--db/query.sql.go47
-rw-r--r--db/schema.sql4
10 files changed, 374 insertions, 0 deletions
diff --git a/bpf/filter/bpf.c b/bpf/filter/bpf.c
new file mode 100644
index 0000000..ae62cb8
--- /dev/null
+++ b/bpf/filter/bpf.c
@@ -0,0 +1,82 @@
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+
+#include <bpf/bpf_endian.h>
+#include <bpf/bpf_helpers.h>
+
+#define MAX_MAP_ENTRIES 4096
+
+char __license[] SEC("license") = "GPL";
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, MAX_MAP_ENTRIES);
+ __type(key, __u64); // blocked mac address
+ // i just like her for the o(1) key lookup
+ // we don't care about the value
+ __type(value, __u16);
+} mac_blacklist_map SEC(".maps");
+
+static __always_inline __u64 nchar6_to_u64(unsigned char bytes[6])
+{
+ union {
+ char bytes[6];
+ __u64 i;
+ } ret;
+
+ ret.i = 0;
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ ret.bytes[0] = bytes[5];
+ ret.bytes[1] = bytes[4];
+ ret.bytes[2] = bytes[3];
+ ret.bytes[3] = bytes[2];
+ ret.bytes[4] = bytes[1];
+ ret.bytes[5] = bytes[0];
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ ret.bytes[0] = bytes[0];
+ ret.bytes[1] = bytes[1];
+ ret.bytes[2] = bytes[2];
+ ret.bytes[3] = bytes[3];
+ ret.bytes[4] = bytes[4];
+ ret.bytes[5] = bytes[5];
+#endif
+
+ return ret.i;
+}
+
+static __always_inline int mac_src_parse(struct xdp_md *ctx, __u64 *mac)
+{
+ __u64 len, *usage;
+
+ void *data_end = (void *)(long)ctx->data_end;
+ struct ethhdr *eth = (void *)(long)ctx->data;
+
+ if ((void *) (eth + 1) > data_end)
+ return -1;
+
+ if (eth->h_proto != bpf_htons(ETH_P_IP) &&
+ eth->h_proto != bpf_htons(ETH_P_IPV6)) {
+ return -1;
+ }
+
+ *mac = nchar6_to_u64(eth->h_source);
+ return 0;
+}
+
+SEC("xdp")
+int mac_filter(struct xdp_md *ctx)
+{
+ __u64 mac;
+ int ret, *blocked;
+
+ ret = mac_src_parse(ctx, &mac);
+ if (ret < 0)
+ return XDP_PASS;
+
+ blocked = bpf_map_lookup_elem(&mac_blacklist_map, &mac);
+ if (blocked)
+ return XDP_DROP;
+
+ return XDP_PASS;
+}
diff --git a/bpf/filter/bpf_bpfel.go b/bpf/filter/bpf_bpfel.go
new file mode 100644
index 0000000..53ec23f
--- /dev/null
+++ b/bpf/filter/bpf_bpfel.go
@@ -0,0 +1,119 @@
+// Code generated by bpf2go; DO NOT EDIT.
+//go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64
+
+package filter
+
+import (
+ "bytes"
+ _ "embed"
+ "fmt"
+ "io"
+
+ "github.com/cilium/ebpf"
+)
+
+// loadBpf returns the embedded CollectionSpec for bpf.
+func loadBpf() (*ebpf.CollectionSpec, error) {
+ reader := bytes.NewReader(_BpfBytes)
+ spec, err := ebpf.LoadCollectionSpecFromReader(reader)
+ if err != nil {
+ return nil, fmt.Errorf("can't load bpf: %w", err)
+ }
+
+ return spec, err
+}
+
+// loadBpfObjects loads bpf and converts it into a struct.
+//
+// The following types are suitable as obj argument:
+//
+// *bpfObjects
+// *bpfPrograms
+// *bpfMaps
+//
+// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
+func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
+ spec, err := loadBpf()
+ if err != nil {
+ return err
+ }
+
+ return spec.LoadAndAssign(obj, opts)
+}
+
+// bpfSpecs contains maps and programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type bpfSpecs struct {
+ bpfProgramSpecs
+ bpfMapSpecs
+}
+
+// bpfSpecs contains programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type bpfProgramSpecs struct {
+ MacFilter *ebpf.ProgramSpec `ebpf:"mac_filter"`
+}
+
+// bpfMapSpecs contains maps before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type bpfMapSpecs struct {
+ MacBlacklistMap *ebpf.MapSpec `ebpf:"mac_blacklist_map"`
+}
+
+// bpfObjects contains all objects after they have been loaded into the kernel.
+//
+// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
+type bpfObjects struct {
+ bpfPrograms
+ bpfMaps
+}
+
+func (o *bpfObjects) Close() error {
+ return _BpfClose(
+ &o.bpfPrograms,
+ &o.bpfMaps,
+ )
+}
+
+// bpfMaps contains all maps after they have been loaded into the kernel.
+//
+// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
+type bpfMaps struct {
+ MacBlacklistMap *ebpf.Map `ebpf:"mac_blacklist_map"`
+}
+
+func (m *bpfMaps) Close() error {
+ return _BpfClose(
+ m.MacBlacklistMap,
+ )
+}
+
+// bpfPrograms contains all programs after they have been loaded into the kernel.
+//
+// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
+type bpfPrograms struct {
+ MacFilter *ebpf.Program `ebpf:"mac_filter"`
+}
+
+func (p *bpfPrograms) Close() error {
+ return _BpfClose(
+ p.MacFilter,
+ )
+}
+
+func _BpfClose(closers ...io.Closer) error {
+ for _, closer := range closers {
+ if err := closer.Close(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Do not access this directly.
+//
+//go:embed bpf_bpfel.o
+var _BpfBytes []byte
diff --git a/bpf/filter/bpf_bpfel.o b/bpf/filter/bpf_bpfel.o
new file mode 100644
index 0000000..2167b99
--- /dev/null
+++ b/bpf/filter/bpf_bpfel.o
Binary files differ
diff --git a/bpf/filter/gen.go b/bpf/filter/gen.go
new file mode 100644
index 0000000..d70c549
--- /dev/null
+++ b/bpf/filter/gen.go
@@ -0,0 +1,3 @@
+package filter
+
+//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target bpfel bpf bpf.c
diff --git a/bpf/filter/main.go b/bpf/filter/main.go
new file mode 100644
index 0000000..4df312c
--- /dev/null
+++ b/bpf/filter/main.go
@@ -0,0 +1,95 @@
+package filter
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "net"
+
+ "github.com/cilium/ebpf/link"
+ "sinanmohd.com/redq/db"
+)
+
+type Filter struct {
+ ctxDb context.Context
+ queries *db.Queries
+ objs bpfObjects
+ xdpLink link.Link
+}
+
+func Close(f *Filter) {
+ f.objs.Close()
+ f.xdpLink.Close()
+}
+
+func New(iface *net.Interface, queries *db.Queries, ctxDb context.Context) (*Filter, error) {
+ var err error
+ var f Filter
+
+ if err := loadBpfObjects(&f.objs, nil); err != nil {
+ log.Printf("loading objects: %s", err)
+ return nil, err
+ }
+ defer func() {
+ if err != nil {
+ f.objs.Close()
+ }
+ }()
+
+ f.xdpLink, err = link.AttachXDP(link.XDPOptions{
+ Interface: iface.Index,
+ Program: f.objs.MacFilter,
+ })
+ if err != nil {
+ log.Printf("could not attach TCx program: %s", err)
+ return nil, err
+ }
+ defer func() {
+ if err != nil {
+ f.xdpLink.Close()
+ }
+ }()
+
+ blackList, err := queries.GetMacBlackList(ctxDb)
+ zeros := make([]uint16, len(blackList))
+ _, err = f.objs.bpfMaps.MacBlacklistMap.BatchUpdate(blackList[:], zeros, nil)
+ if err != nil {
+ log.Printf("loading mac blacklist: %s", err)
+ return nil, err
+ }
+
+ f.queries = queries
+ return &f, nil
+}
+
+func (f *Filter) Block(mac uint64) error {
+ err := f.queries.EnterMacBlackList(f.ctxDb, int64(mac))
+ if err != nil {
+ log.Printf("adding mac blacklist: %s", err)
+ return err
+ }
+
+ err = f.objs.bpfMaps.MacBlacklistMap.Put(mac, true)
+ if err != nil {
+ log.Printf("adding mac blacklist: %s", err)
+ return err
+ }
+
+ return nil
+}
+
+func (f *Filter) Unblock(mac uint64) error {
+ err := f.queries.DeleteDnsBlackList(f.ctxDb, fmt.Sprintf("%v", mac))
+ if err != nil {
+ log.Printf("adding mac blacklist: %s", err)
+ return err
+ }
+
+ err = f.objs.bpfMaps.MacBlacklistMap.Delete(mac)
+ if err != nil {
+ log.Printf("adding mac blacklist: %s", err)
+ return err
+ }
+
+ return nil
+}
diff --git a/cmd/main.go b/cmd/main.go
index 5c58d44..dc456ce 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -13,6 +13,7 @@ import (
"sinanmohd.com/redq/db"
"sinanmohd.com/redq/dns"
"sinanmohd.com/redq/bpf/usage"
+ "sinanmohd.com/redq/bpf/filter"
)
func main() {
@@ -37,6 +38,10 @@ func main() {
if err != nil {
os.Exit(0)
}
+ f, err := filter.New(iface, queries, ctx)
+ if err != nil {
+ os.Exit(0)
+ }
u, err := usage.New(iface)
if err != nil {
os.Exit(0)
@@ -47,6 +52,7 @@ func main() {
go func() {
<-sigs
usage.Close(u, queries, ctx)
+ filter.Close(f)
api.Close(a)
os.Exit(0)
}()
diff --git a/db/models.go b/db/models.go
index 1ea2797..3beb527 100644
--- a/db/models.go
+++ b/db/models.go
@@ -12,6 +12,10 @@ type Dnsblacklist struct {
Name string
}
+type Macblacklist struct {
+ Hardwareaddr int64
+}
+
type Usage struct {
Hardwareaddr int64
Starttime pgtype.Timestamp
diff --git a/db/query.sql b/db/query.sql
index f4b17dd..624442f 100644
--- a/db/query.sql
+++ b/db/query.sql
@@ -21,3 +21,17 @@ WHERE Name = $1;
-- name: GetDnsBlackList :many
SELECT * FROM DnsBlackList;
+
+-- name: EnterMacBlackList :exec
+INSERT INTO MacBlackList (
+ HardwareAddr
+) VALUES (
+ $1
+);
+
+-- name: DeleteMacBlackList :exec
+DELETE FROM MacBlackList
+WHERE HardwareAddr = $1;
+
+-- name: GetMacBlackList :many
+SELECT * FROM MacBlackList;
diff --git a/db/query.sql.go b/db/query.sql.go
index 7c28723..57bee9f 100644
--- a/db/query.sql.go
+++ b/db/query.sql.go
@@ -21,6 +21,16 @@ func (q *Queries) DeleteDnsBlackList(ctx context.Context, name string) error {
return err
}
+const deleteMacBlackList = `-- name: DeleteMacBlackList :exec
+DELETE FROM MacBlackList
+WHERE HardwareAddr = $1
+`
+
+func (q *Queries) DeleteMacBlackList(ctx context.Context, hardwareaddr int64) error {
+ _, err := q.db.Exec(ctx, deleteMacBlackList, hardwareaddr)
+ return err
+}
+
const enterDnsBlackList = `-- name: EnterDnsBlackList :exec
INSERT INTO DnsBlackList (
Name
@@ -34,6 +44,19 @@ func (q *Queries) EnterDnsBlackList(ctx context.Context, name string) error {
return err
}
+const enterMacBlackList = `-- name: EnterMacBlackList :exec
+INSERT INTO MacBlackList (
+ HardwareAddr
+) VALUES (
+ $1
+)
+`
+
+func (q *Queries) EnterMacBlackList(ctx context.Context, hardwareaddr int64) error {
+ _, err := q.db.Exec(ctx, enterMacBlackList, hardwareaddr)
+ return err
+}
+
const enterUsage = `-- name: EnterUsage :exec
INSERT INTO Usage (
HardwareAddr, StartTime, StopTime, Egress, Ingress
@@ -85,6 +108,30 @@ func (q *Queries) GetDnsBlackList(ctx context.Context) ([]string, error) {
return items, nil
}
+const getMacBlackList = `-- name: GetMacBlackList :many
+SELECT hardwareaddr FROM MacBlackList
+`
+
+func (q *Queries) GetMacBlackList(ctx context.Context) ([]int64, error) {
+ rows, err := q.db.Query(ctx, getMacBlackList)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []int64
+ for rows.Next() {
+ var hardwareaddr int64
+ if err := rows.Scan(&hardwareaddr); err != nil {
+ return nil, err
+ }
+ items = append(items, hardwareaddr)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
const getUsage = `-- name: GetUsage :one
SELECT SUM(Ingress) AS Ingress, SUM(Egress) AS Egress FROM Usage
`
diff --git a/db/schema.sql b/db/schema.sql
index c8c35a1..78668c6 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -9,3 +9,7 @@ CREATE TABLE IF NOT EXISTS Usage (
CREATE TABLE IF NOT EXISTS DnsBlackList (
Name TEXT NOT NULL UNIQUE
);
+
+CREATE TABLE IF NOT EXISTS MacBlackList (
+ HardwareAddr BIGINT NOT NULL UNIQUE
+);