Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
G
GNS3_unikernel_testbed
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
NGUYEN Do Duc Anh
GNS3_unikernel_testbed
Commits
5bc2af77
Commit
5bc2af77
authored
9 months ago
by
NGUYEN Do Duc Anh
Browse files
Options
Downloads
Patches
Plain Diff
add firewall unikernel + initiate network
parent
57cd85f0
No related branches found
No related tags found
No related merge requests found
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
simple-fw/config.ml
+44
-0
44 additions, 0 deletions
simple-fw/config.ml
simple-fw/rules.ml
+269
-0
269 additions, 0 deletions
simple-fw/rules.ml
ubuntu_base/initiate
+20
-0
20 additions, 0 deletions
ubuntu_base/initiate
with
333 additions
and
0 deletions
simple-fw/config.ml
0 → 100644
+
44
−
0
View file @
5bc2af77
(* mirage >= 4.7.0 & < 4.8.0 *)
open
Mirage
(* we need two network interfaces: a public side and a private side *)
(* a bit of magic: currently, multiple networks only work on Unix and Xen
backends, so we can get away with this indexes-as-numbers-as-strings
silliness.
See https://github.com/mirage/mirage/issues/645 *)
let
public_netif
=
Key
.(
if_impl
is_solo5
(
netif
~
group
:
"public"
"public"
)
(
netif
~
group
:
"public"
"0"
))
let
private_netif
=
Key
.(
if_impl
is_solo5
(
netif
~
group
:
"private"
"private"
)
(
netif
~
group
:
"private"
"1"
))
(* build ethernet interfaces on top of those network interfaces *)
let
public_ethernet
=
ethif
public_netif
let
private_ethernet
=
ethif
private_netif
let
packages
=
[
package
"ethernet"
;
package
"ipaddr"
;
package
~
min
:
"8.2.0"
~
sublibs
:
[
"ipv4"
;
"tcp"
]
"tcpip"
;
(* There was a breaking API changes in the 8.2 series *)
package
"logs"
;
(* package ~min:"4.0.0" "mirage-runtime"; *)
(* We want to avoid runtime argument at any cost *)
]
(* our unikernel needs to know about physical network, and ethernet modules
for each interface. Even though these modules will be the same for both
interfaces in our case, we have to pass them separately. *)
let
main
=
main
"Unikernel.Main"
~
packages
(
network
@->
network
@->
ethernet
@->
ethernet
@->
random
@->
mclock
@->
job
)
(* we need to pass each of the network-related impls we've made to the
unikernel, so that it can start the appropriate listeners. *)
let
()
=
register
"simple-fw"
[
main
$
public_netif
$
private_netif
$
public_ethernet
$
private_ethernet
$
default_random
$
default_monotonic_clock
]
\ No newline at end of file
This diff is collapsed.
Click to expand it.
simple-fw/rules.ml
0 → 100644
+
269
−
0
View file @
5bc2af77
let
log
=
Logs
.
Src
.
create
"rules"
~
doc
:
"FW rules management"
module
Log
=
(
val
Logs
.
src_log
log
:
Logs
.
LOG
)
type
decision
=
ACCEPT
|
DROP
(* TODO:
| ESTABLISHED
| RELATED will need more work... *)
type
rule
=
{
src
:
Ipaddr
.
V4
.
Prefix
.
t
;
psrc
:
int
;
dst
:
Ipaddr
.
V4
.
Prefix
.
t
;
pdst
:
int
;
proto
:
Ipv4_packet
.
protocol
option
;
action
:
decision
option
;
}
let
rule_to_string
r
=
String
.
concat
" "
[
Ipaddr
.
V4
.
Prefix
.
to_string
r
.
src
;
":"
;
(
match
r
.
psrc
with
0
->
"ANY"
|
x
->
Int
.
to_string
x
);
"->"
;
Ipaddr
.
V4
.
Prefix
.
to_string
r
.
dst
;
":"
;
(
match
r
.
pdst
with
0
->
"ANY"
|
x
->
Int
.
to_string
x
);
"("
;
(
match
r
.
proto
with
|
Some
`TCP
->
"TCP"
|
Some
`UDP
->
"UDP"
|
Some
`ICMP
->
"ICMP"
|
None
->
"ANY"
);
") :"
;
(
match
r
.
action
with
|
Some
ACCEPT
->
"ACCEPT"
|
Some
DROP
->
"DROP"
|
None
->
"XXX"
(* Should not be printed ever... add an assert false?*)
);
]
(* A match is either the target is joker or the ip is in the target network *)
let
match_ip
ip
target
=
target
=
Ipaddr
.
V4
.
Prefix
.
global
||
Ipaddr
.
V4
.
Prefix
.
mem
ip
target
type
cb
=
Ipaddr
.
V4
.
t
*
Cstruct
.
t
->
unit
Lwt
.
t
type
t
=
{
mutable
l
:
rule
list
;
default
:
bool
}
let
init
default
=
{
l
=
[
{
src
=
Ipaddr
.
V4
.
Prefix
.
of_string_exn
"10.0.0.0/24"
;
psrc
=
0
;
dst
=
Ipaddr
.
V4
.
Prefix
.
of_string_exn
"10.0.0.0/24"
;
pdst
=
7070
;
proto
=
Some
`TCP
;
action
=
Some
ACCEPT
;
};
{
src
=
Ipaddr
.
V4
.
Prefix
.
of_string_exn
"10.0.0.0/24"
;
psrc
=
7070
;
dst
=
Ipaddr
.
V4
.
Prefix
.
of_string_exn
"10.0.0.0/24"
;
pdst
=
0
;
proto
=
Some
`TCP
;
action
=
Some
ACCEPT
;
};
{
src
=
Ipaddr
.
V4
.
Prefix
.
of_string_exn
"10.0.0.0/24"
;
psrc
=
0
;
dst
=
Ipaddr
.
V4
.
Prefix
.
of_string_exn
"10.0.0.0/24"
;
pdst
=
8080
;
proto
=
Some
`UDP
;
action
=
Some
ACCEPT
;
};
{
src
=
Ipaddr
.
V4
.
Prefix
.
of_string_exn
"10.0.0.0/24"
;
psrc
=
0
;
dst
=
Ipaddr
.
V4
.
Prefix
.
of_string_exn
"10.0.0.0/24"
;
pdst
=
0
;
proto
=
Some
`ICMP
;
action
=
Some
ACCEPT
;
};
{
src
=
Ipaddr
.
V4
.
Prefix
.
of_string_exn
"10.0.0.0/24"
;
psrc
=
0
;
dst
=
Ipaddr
.
V4
.
Prefix
.
global
;
pdst
=
0
;
proto
=
Some
`UDP
;
action
=
Some
ACCEPT
;
};
{
src
=
Ipaddr
.
V4
.
Prefix
.
global
;
psrc
=
0
;
dst
=
Ipaddr
.
V4
.
Prefix
.
global
;
pdst
=
0
;
proto
=
None
;
action
=
Some
DROP
;
};
];
default
;
}
(* Here we apply, for easy testing, a default to accept last ressort rule :x
let default_accept cb (dest, packet) =
Log.debug (fun f -> f "Default rule accept packet to %a..." Ipaddr.V4.pp dest);
cb (dest, packet)
(* The default to drop last ressort rule would be *)
let default_drop _cb (dest, _packet) =
Log.debug (fun f -> f "Default rule drop packet to %a..." Ipaddr.V4.pp dest);
Lwt.return_unit *)
(* The update packet is (as a UDP packet):
2B port source + 2B port dest + 2B len + 2B crc
+ 4B magic string "1234"
+ 1B insert or append (INSERT=0, APPEND=1)
+ 1B number of the following
+ 4B IP source (all 0s is joker)
+ 4B IP dest (all 0s is joker)
+ 1B source netmask (0 is joker) + 1B destination netmask (0 is joker)
+ 2B port source (0 is joker)
+ 2B destination source (0 is joker)
+ 1B the protocol code (ICMP=1, TCP=6, UDP=17, ANY=other values)
+ 1B the decision (0=DROP, 1=ACCEPT, anything else is a failure)
One can send such a packet with:
python -c "import sys; sys.stdout.buffer.write(bytearray([49, 50, 51, 52, 0, 2, 10, 11, 12, 13, 192, 168, 0, 0, 24, 24, 0, 53, 0, 0, 6, 0, 172, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 1]))" | nc -u 10.0.0.2 1234
This would append the rules:
10.11.12.13/24 : 53 -> 192.168.0.0/24 : ANY ( TCP ) : DROP
172.16.0.0/12 : ANY -> 0.0.0.0/0 : ANY ( ANY ) : ACCEPT
*)
let
magic_hdr
=
Int32
.
of_string
"0x31323334"
let
magic_is_present
payload
=
Cstruct
.
BE
.
get_uint16
payload
2
=
1234
&&
(* our port must be 1234 *)
Cstruct
.
BE
.
get_uint32
payload
8
=
magic_hdr
(* I currently don't bother to check the crc... *)
let
update
t
payload
=
(* Skip the UDP header *)
let
payload
=
Cstruct
.
shift
payload
8
in
let
ins_or_app
=
Cstruct
.
get_uint8
payload
4
in
if
ins_or_app
<>
0
&&
ins_or_app
<>
1
then
Log
.
err
(
fun
f
->
f
"Don't know what to do with the rules"
)
else
let
n
=
Cstruct
.
get_uint8
payload
5
in
let
rec
extract_rule
acc
payload
=
let
rule_size
=
16
in
if
Cstruct
.
length
payload
<
rule_size
then
(* TODO: what is the expected behaviour if there is not already consummed data? *)
acc
else
let
src
=
Ipaddr
.
V4
.
of_int32
(
Cstruct
.
BE
.
get_uint32
payload
0
)
in
let
dst
=
Ipaddr
.
V4
.
of_int32
(
Cstruct
.
BE
.
get_uint32
payload
4
)
in
let
src_mask
=
Cstruct
.
get_uint8
payload
8
in
let
src
=
Ipaddr
.
V4
.
Prefix
.
make
src_mask
src
in
let
dst_mask
=
Cstruct
.
get_uint8
payload
9
in
let
dst
=
Ipaddr
.
V4
.
Prefix
.
make
dst_mask
dst
in
let
psrc
=
Cstruct
.
BE
.
get_uint16
payload
10
in
let
pdst
=
Cstruct
.
BE
.
get_uint16
payload
12
in
let
proto
=
match
Cstruct
.
get_uint8
payload
14
with
|
1
->
Some
`ICMP
|
6
->
Some
`TCP
|
17
->
Some
`UDP
|
_
->
None
(* FIXME: distinguish a value error from a special ANY case? *)
in
let
action
=
match
Cstruct
.
get_uint8
payload
15
with
|
0
->
Some
DROP
|
1
->
Some
ACCEPT
|
_
->
None
in
let
r
=
{
src
;
dst
;
psrc
;
pdst
;
proto
;
action
}
in
(* Fail on the first unrecognized rule *)
if
action
=
None
then
(
Log
.
err
(
fun
f
->
f
"Cannot recognize rule: %s"
(
rule_to_string
r
));
[]
)
else
(
Log
.
debug
(
fun
f
->
f
"Recognized rule: %s"
(
rule_to_string
r
));
extract_rule
(
r
::
acc
)
(
Cstruct
.
shift
payload
rule_size
))
in
let
r
=
extract_rule
[]
(
Cstruct
.
shift
payload
6
)
in
match
(
ins_or_app
,
r
)
with
|
_
,
_
when
List
.
length
r
<>
n
->
Log
.
err
(
fun
f
->
f
"Not enough rules %d vs. %d"
(
List
.
length
r
)
n
)
|
0
,
r
->
t
.
l
<-
List
.
append
r
t
.
l
|
1
,
r
->
t
.
l
<-
List
.
append
t
.
l
r
|
_
,
_
->
Log
.
err
(
fun
f
->
f
"Don't know chat to do with the rules"
);
assert
false
(* The code should not go there...*)
let
is_matching_port
packet
proto
r_psrc
r_pdst
=
match
(
r_psrc
,
r_pdst
,
proto
)
with
|
0
,
0
,
_
->
true
|
_
,
_
,
6
->
(
(* TCP *)
match
Tcp
.
Tcp_packet
.
Unmarshal
.
of_cstruct
packet
with
|
Result
.
Error
s
->
Logs
.
err
(
fun
m
->
m
"Can't parse TCP packet: %s"
s
);
false
|
Result
.
Ok
(
tcp_hdr
,
_payload
)
->
(
r_psrc
=
0
||
tcp_hdr
.
src_port
=
r_psrc
)
&&
(
r_pdst
=
0
||
tcp_hdr
.
dst_port
=
r_pdst
))
|
_
,
_
,
17
->
(
(* UDP *)
match
Udp_packet
.
Unmarshal
.
of_cstruct
packet
with
|
Result
.
Error
s
->
Logs
.
err
(
fun
m
->
m
"Can't parse UDP packet: %s"
s
);
false
|
Result
.
Ok
(
udp_hdr
,
_payload
)
->
(
r_psrc
=
0
||
udp_hdr
.
src_port
=
r_psrc
)
&&
(
r_pdst
=
0
||
udp_hdr
.
dst_port
=
r_pdst
))
|
_
,
_
,
1
->
true
(* ICMP *)
|
_
->
true
(* Takes an ipv4 header [ipv4_hdr] and the whole IPv4 [packet] (containing the header).
We want to filter out any packet matching the [filters] list, and if not filtered,
transfer it (unchanged) to [forward_to].
NOTE: We know the packet is not for us. *)
let
filter
t
(
ipv4_hdr
,
packet
)
=
let
rec
apply_rules
:
bool
->
Ipv4_packet
.
t
*
Cstruct
.
t
->
rule
list
->
bool
=
fun
default
(
ipv4_hdr
,
packet
)
filter_rules
->
match
filter_rules
with
(* If the list is empty -> apply default (true or false) action *)
|
[]
->
default
(* If the packet matches the condition and has an accept action *)
|
{
src
;
psrc
;
dst
;
pdst
;
proto
;
action
=
Some
ACCEPT
}
::
_
when
match_ip
ipv4_hdr
.
src
src
&&
match_ip
ipv4_hdr
.
dst
dst
&&
(
proto
=
None
||
Ipv4_packet
.
Unmarshal
.
int_to_protocol
ipv4_hdr
.
Ipv4_packet
.
proto
=
proto
)
&&
is_matching_port
packet
ipv4_hdr
.
Ipv4_packet
.
proto
psrc
pdst
(* TODO: also check the ports :) *)
->
Log
.
debug
(
fun
f
->
f
"Accept a packet from %a to %a..."
Ipaddr
.
V4
.
pp
ipv4_hdr
.
src
Ipaddr
.
V4
.
pp
ipv4_hdr
.
dst
);
true
(* Otherwise the packet matches and the action is drop *)
|
{
src
;
psrc
;
dst
;
pdst
;
proto
;
action
=
Some
DROP
}
::
_
when
match_ip
ipv4_hdr
.
src
src
&&
match_ip
ipv4_hdr
.
dst
dst
&&
(
proto
=
None
||
Ipv4_packet
.
Unmarshal
.
int_to_protocol
ipv4_hdr
.
Ipv4_packet
.
proto
=
proto
)
&&
is_matching_port
packet
ipv4_hdr
.
Ipv4_packet
.
proto
psrc
pdst
(* TODO: also check the ports :) *)
->
Log
.
debug
(
fun
f
->
f
"Filter out a packet from %a to %a..."
Ipaddr
.
V4
.
pp
ipv4_hdr
.
src
Ipaddr
.
V4
.
pp
ipv4_hdr
.
dst
);
false
(* Or finally the packet does not match the condition *)
|
_
::
tail
->
apply_rules
default
(
ipv4_hdr
,
packet
)
tail
in
apply_rules
t
.
default
(
ipv4_hdr
,
packet
)
t
.
l
\ No newline at end of file
This diff is collapsed.
Click to expand it.
ubuntu_base/initiate
0 → 100644
+
20
−
0
View file @
5bc2af77
sudo brctl addbr br0
sudo ip link set br0 up
for i in {3..11}; do
sudo brctl addif br0 ens$i
done
sudo ip tuntap add dev private12 mode tap
sudo ip link set dev private12 up
sudo ip tuntap add dev public12 mode tap
sudo ip link set dev public12 up
sudo brctl addbr br12
sudo ip link set dev br12 up
sudo brctl addif br12 ens12
sudo brctl addif br12 public12
sudo brctl addif br0 private12
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment