/
ip.go
147 lines (135 loc) · 3.81 KB
/
ip.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package exprs
import (
"fmt"
"net"
"strings"
"github.com/evilsocket/opensnitch/daemon/firewall/config"
"github.com/google/nftables/expr"
"golang.org/x/sys/unix"
)
// NewExprIP returns a new IP expression.
// You can use multiple statements to specify daddr + saddr, or combine them
// in a single statement expression:
// Example 1 (filtering by source and dest address):
// "Name": "ip",
// "Values": [ {"Key": "saddr": "Value": "1.2.3.4"},{"Key": "daddr": "Value": "1.2.3.5"} ]
// Example 2 (filtering by multiple dest addrs IPs):
// "Name": "ip",
// "Values": [
// {"Key": "daddr": "Value": "1.2.3.4"},
// {"Key": "daddr": "Value": "1.2.3.5"}
// ]
// Example 3 (filtering by network range):
// "Name": "ip",
// "Values": [
// {"Key": "daddr": "Value": "1.2.3.4-1.2.9.254"}
// ]
// TODO (filter by multiple dest addrs separated by commas):
// "Values": [
// {"Key": "daddr": "Value": "1.2.3.4,1.2.9.254"}
// ]
func NewExprIP(family string, ipOptions []*config.ExprValues, cmpOp expr.CmpOp) (*[]expr.Any, error) {
var exprIP []expr.Any
// if the table family is inet, we need to specify the protocol of the IP being added.
if family == NFT_FAMILY_INET {
exprIP = append(exprIP, &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1})
exprIP = append(exprIP, &expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.NFPROTO_IPV4}})
}
for _, ipOpt := range ipOptions {
// TODO: ipv6
switch ipOpt.Key {
case NFT_SADDR, NFT_DADDR:
payload := getExprIPPayload(ipOpt.Key)
exprIP = append(exprIP, payload)
if strings.Index(ipOpt.Value, "-") == -1 {
exprIPtemp, err := getExprIP(ipOpt.Value, cmpOp)
if err != nil {
return nil, err
}
exprIP = append(exprIP, *exprIPtemp...)
} else {
exprIPtemp, err := getExprRangeIP(ipOpt.Value, cmpOp)
if err != nil {
return nil, err
}
exprIP = append(exprIP, *exprIPtemp...)
}
case NFT_PROTOCOL:
payload := getExprIPPayload(ipOpt.Key)
exprIP = append(exprIP, payload)
protoCode, err := getProtocolCode(ipOpt.Value)
if err != nil {
return nil, err
}
exprIP = append(exprIP, []expr.Any{
&expr.Cmp{
Op: cmpOp,
Register: 1,
Data: []byte{byte(protoCode)},
},
}...)
}
}
return &exprIP, nil
}
func getExprIPPayload(what string) *expr.Payload {
switch what {
case NFT_PROTOCOL:
return &expr.Payload{
DestRegister: 1,
Offset: 9, // daddr
Base: expr.PayloadBaseNetworkHeader,
Len: 1, // 16 ipv6
}
case NFT_DADDR:
// NOTE 1: if "what" is daddr and SourceRegister is part of the Payload{} expression,
// the rule is not added.
return &expr.Payload{
DestRegister: 1,
Offset: 16, // daddr
Base: expr.PayloadBaseNetworkHeader,
Len: 4, // 16 ipv6
}
default:
return &expr.Payload{
SourceRegister: 1,
DestRegister: 1,
Offset: 12, // saddr
Base: expr.PayloadBaseNetworkHeader,
Len: 4, // 16 ipv6
}
}
}
// Supported IP types: a.b.c.d, a.b.c.d-w.x.y.z
// TODO: support IPs separated by commas: a.b.c.d, e.f.g.h,...
func getExprIP(value string, cmpOp expr.CmpOp) (*[]expr.Any, error) {
ip := net.ParseIP(value)
if ip == nil {
return nil, fmt.Errorf("Invalid IP: %s", value)
}
return &[]expr.Any{
&expr.Cmp{
Op: cmpOp,
Register: 1,
Data: ip.To4(),
},
}, nil
}
// Supported IP types: a.b.c.d, a.b.c.d-w.x.y.z
// TODO: support IPs separated by commas: a.b.c.d, e.f.g.h,...
func getExprRangeIP(value string, cmpOp expr.CmpOp) (*[]expr.Any, error) {
ips := strings.Split(value, "-")
ipSrc := net.ParseIP(ips[0])
ipDst := net.ParseIP(ips[1])
if ipSrc == nil || ipDst == nil {
return nil, fmt.Errorf("Invalid IPs range: %v", ips)
}
return &[]expr.Any{
&expr.Range{
Op: cmpOp,
Register: 1,
FromData: ipSrc.To4(),
ToData: ipDst.To4(),
},
}, nil
}