forked from checkpoint-restore/criu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
netfilter.c
145 lines (123 loc) · 3.59 KB
/
netfilter.c
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
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
#include "asm/types.h"
#include "util.h"
#include "list.h"
#include "files.h"
#include "netfilter.h"
#include "sockets.h"
#include "sk-inet.h"
#include "kerndat.h"
static char buf[512];
/*
* Need to configure simple netfilter rules for blocking connections
* ANy brave soul to write it using xtables-devel?
*/
#define NF_CONN_CMD "%s %s -t filter %s %s --protocol tcp " \
"--source %s --sport %d --destination %s --dport %d -j DROP"
static char iptable_cmd_ipv4[] = "iptables";
static char iptable_cmd_ipv6[] = "ip6tables";
void preload_netfilter_modules(void)
{
int fd = -1;
/* same as socket modules, ip_tables and ip6_tables will be loaded by
* CRIU, so we should try and preload these as well.
*/
fd = open("/dev/null", O_RDWR);
if (fd < 0) {
fd = -1;
pr_perror("failed to open /dev/null, using log fd for net module preload");
}
cr_system(fd, fd, fd, iptable_cmd_ipv4,
(char *[]) { iptable_cmd_ipv4, "-L", "-n", NULL}, 0);
cr_system(fd, fd, fd, iptable_cmd_ipv6,
(char *[]) { iptable_cmd_ipv6, "-L", "-n", NULL}, 0);
close_safe(&fd);
}
static int nf_connection_switch_raw(int family, u32 *src_addr, u16 src_port,
u32 *dst_addr, u16 dst_port,
bool input, bool lock)
{
char sip[INET_ADDR_LEN], dip[INET_ADDR_LEN];
char *cmd;
char *argv[4] = { "sh", "-c", buf, NULL };
int ret;
switch (family) {
case AF_INET:
cmd = iptable_cmd_ipv4;
break;
case AF_INET6:
cmd = iptable_cmd_ipv6;
break;
default:
pr_err("Unknown socket family %d\n", family);
return -1;
};
if (!inet_ntop(family, (void *)src_addr, sip, INET_ADDR_LEN) ||
!inet_ntop(family, (void *)dst_addr, dip, INET_ADDR_LEN)) {
pr_perror("nf: Can't translate ip addr");
return -1;
}
snprintf(buf, sizeof(buf), NF_CONN_CMD, cmd,
kdat.has_xtlocks ? "-w" : "",
lock ? "-A" : "-D",
input ? "INPUT" : "OUTPUT",
dip, (int)dst_port, sip, (int)src_port);
pr_debug("\tRunning iptables [%s]\n", buf);
/*
* cr_system is used here, because it blocks SIGCHLD before waiting
* a child and the child can't be waited from SIGCHLD handler.
*/
ret = cr_system(-1, -1, -1, "sh", argv, 0);
if (ret < 0 || !WIFEXITED(ret) || WEXITSTATUS(ret)) {
pr_err("Iptables configuration failed\n");
return -1;
}
pr_info("%s %s:%d - %s:%d connection\n", lock ? "Locked" : "Unlocked",
sip, (int)src_port, dip, (int)dst_port);
return 0;
}
static int nf_connection_switch(struct inet_sk_desc *sk, bool lock)
{
int ret = 0;
ret = nf_connection_switch_raw(sk->sd.family,
sk->src_addr, sk->src_port,
sk->dst_addr, sk->dst_port, true, lock);
if (ret)
return -1;
ret = nf_connection_switch_raw(sk->sd.family,
sk->dst_addr, sk->dst_port,
sk->src_addr, sk->src_port, false, lock);
if (ret) /* rollback */
nf_connection_switch_raw(sk->sd.family,
sk->src_addr, sk->src_port,
sk->dst_addr, sk->dst_port, true, !lock);
return ret;
}
int nf_lock_connection(struct inet_sk_desc *sk)
{
return nf_connection_switch(sk, true);
}
int nf_unlock_connection(struct inet_sk_desc *sk)
{
return nf_connection_switch(sk, false);
}
int nf_unlock_connection_info(struct inet_sk_info *si)
{
int ret = 0;
ret |= nf_connection_switch_raw(si->ie->family,
si->ie->src_addr, si->ie->src_port,
si->ie->dst_addr, si->ie->dst_port, true, false);
ret |= nf_connection_switch_raw(si->ie->family,
si->ie->dst_addr, si->ie->dst_port,
si->ie->src_addr, si->ie->src_port, false, false);
/*
* rollback nothing in case of any error,
* because nobody checks errors of this function
*/
return ret;
}