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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
| | // SPDX-License-Identifier: GPL-2.0-or-later
/* PASST - Plug A Simple Socket Transport
* for qemu/UNIX domain socket mode
*
* PASTA - Pack A Subtle Tap Abstraction
* for network namespace/tap device mode
*
* PESTO - Programmable Extensible Socket Translation Orchestrator
* front-end for passt(1) and pasta(1) forwarding configuration
*
* fwd_rule.c - Helpers for working with forwarding rule specifications
*
* Copyright Red Hat
* Author: David Gibson <david@gibson.dropbear.id.au>
*/
#include <stdio.h>
#include "serialise.h"
#include "ports.h"
#include "fwd_rule.h"
#include "log.h"
/**
* fwd_rule_addr() - Return match address for a rule
* @rule: Forwarding rule
*
* Return: matching address for rule, NULL if it matches all addresses
*/
const union inany_addr *fwd_rule_addr(const struct fwd_rule *rule)
{
if (rule->flags & FWD_DUAL_STACK_ANY)
return NULL;
return &rule->addr;
}
/**
* fwd_rule_ntop() - Format forwarding rule as a string
* @rule: Rule to format
* @dst: Buffer to store output (should have FWD_RULE_STRLEN bytes)
* @size: Size of @dst
*/
const char *fwd_rule_ntop(const struct fwd_rule *rule, char *dst, size_t size)
{
const char *percent = *rule->ifname ? "%" : "";
const char *weak = "", *scan = "";
char addr[INANY_ADDRSTRLEN];
int len;
inany_ntop(fwd_rule_addr(rule), addr, sizeof(addr));
if (rule->flags & FWD_WEAK)
weak = " (best effort)";
if (rule->flags & FWD_SCAN)
scan = " (auto-scan)";
if (rule->first == rule->last) {
len = snprintf(dst, size,
"%s [%s]%s%s:%hu => %hu %s%s",
ipproto_name(rule->proto), addr, percent,
rule->ifname, rule->first, rule->to, weak, scan);
} else {
in_port_t tolast = rule->last - rule->first + rule->to;
len = snprintf(dst, size,
"%s [%s]%s%s:%hu-%hu => %hu-%hu %s%s",
ipproto_name(rule->proto), addr, percent,
rule->ifname, rule->first, rule->last,
rule->to, tolast, weak, scan);
}
if (len < 0 || (size_t)len >= size)
return NULL;
return dst;
}
/**
* fwd_rule_add() - Add a rule to a forwarding table
* @fwd: Table to add to
* @proto: Protocol to forward
* @flags: Flags for this entry
* @addr: Our address to forward (NULL for both 0.0.0.0 and ::)
* @ifname: Only forward from this interface name, if non-empty
* @first: First port number to forward
* @last: Last port number to forward
* @to: First port of target port range to map to
*/
void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags,
const union inany_addr *addr, const char *ifname,
in_port_t first, in_port_t last, in_port_t to)
{
/* Flags which can be set from the caller */
const uint8_t allowed_flags = FWD_WEAK | FWD_SCAN;
unsigned num = (unsigned)last - first + 1;
struct fwd_rule_state *new;
unsigned i, port;
assert(!(flags & ~allowed_flags));
if (fwd->count >= ARRAY_SIZE(fwd->rules))
die("Too many port forwarding ranges");
if ((fwd->sock_count + num) > ARRAY_SIZE(fwd->socks))
die("Too many listening sockets");
/* Check for any conflicting entries */
for (i = 0; i < fwd->count; i++) {
char newstr[INANY_ADDRSTRLEN], rulestr[INANY_ADDRSTRLEN];
struct fwd_rule_state *rule = &fwd->rules[i];
if (proto != rule->rule.proto)
/* Non-conflicting protocols */
continue;
if (!inany_matches(addr, fwd_rule_addr(&rule->rule)))
/* Non-conflicting addresses */
continue;
if (last < rule->rule.first || rule->rule.last < first)
/* Port ranges don't overlap */
continue;
die("Forwarding configuration conflict: %s/%u-%u versus %s/%u-%u",
inany_ntop(addr, newstr, sizeof(newstr)), first, last,
inany_ntop(fwd_rule_addr(&rule->rule),
rulestr, sizeof(rulestr)),
rule->rule.first, rule->rule.last);
}
new = &fwd->rules[fwd->count++];
new->rule.proto = proto;
new->rule.flags = flags;
if (addr) {
new->rule.addr = *addr;
} else {
new->rule.addr = inany_any6;
new->rule.flags |= FWD_DUAL_STACK_ANY;
}
memset(new->rule.ifname, 0, sizeof(new->rule.ifname));
if (ifname) {
int ret;
ret = snprintf(new->rule.ifname, sizeof(new->rule.ifname),
"%s", ifname);
if (ret <= 0 || (size_t)ret >= sizeof(new->rule.ifname))
die("Invalid interface name: %s", ifname);
}
assert(first <= last);
new->rule.first = first;
new->rule.last = last;
new->rule.to = to;
new->socks = &fwd->socks[fwd->sock_count];
fwd->sock_count += num;
for (port = new->rule.first; port <= new->rule.last; port++)
new->socks[port - new->rule.first] = -1;
}
/**
* fwd_rule_seread() - Read erialised rule from an fd
* @fd: fd to serialise to
* @rule: Buffer to store rule into
*
* Return: 0 on success, -1 on error (with errno set)
*/
int fwd_rule_seread(int fd, struct fwd_rule *rule)
{
if (seread_var(fd, rule))
return -1;
/* Byteswap for host */
rule->first = ntohs(rule->first);
rule->last = ntohs(rule->last);
rule->to = htons(rule->to);
return 0;
}
/**
* fwd_rule_sewrite() - Serialise rule to an fd
* @fd: fd to serialise to
* @rule: Rule to send
*
* Return: 0 on success, -1 on error (with errno set)
*/
int fwd_rule_sewrite(int fd, const struct fwd_rule *rule)
{
struct fwd_rule tmp = *rule;
/* Byteswap for transport */
tmp.first = htons(tmp.first);
tmp.last = htons(tmp.last);
tmp.to = htons(tmp.to);
return sewrite_var(fd, &tmp);
}
|