diff --git a/lib/Sys/Ebpf/Link/Netlink/Constants/Rtnetlink.pm b/lib/Sys/Ebpf/Link/Netlink/Constants/Rtnetlink.pm new file mode 100644 index 0000000..057b279 --- /dev/null +++ b/lib/Sys/Ebpf/Link/Netlink/Constants/Rtnetlink.pm @@ -0,0 +1,96 @@ +package Sys::Ebpf::Link::Netlink::Constants::Rtnetlink; + +use strict; +use warnings; +use utf8; + +use Exporter 'import'; + +# cf. https://github.com/torvalds/linux/blob/master/include/uapi/linux/rtnetlink.h +my %constants = ( + + # /* Routing table identifiers. */ + # /* Types of messages */ + 'RTM_BASE' => 16, + 'RTM_NEWLINK' => 16, + 'RTM_DELLINK' => 17, + 'RTM_GETLINK' => 18, + 'RTM_SETLINK' => 19, + 'RTM_NEWADDR' => 20, + 'RTM_DELADDR' => 21, + 'RTM_GETADDR' => 22, + 'RTM_NEWROUTE' => 24, + 'RTM_DELROUTE' => 25, + 'RTM_GETROUTE' => 26, + 'RTM_NEWNEIGH' => 28, + 'RTM_DELNEIGH' => 29, + 'RTM_GETNEIGH' => 30, + 'RTM_NEWRULE' => 32, + 'RTM_DELRULE' => 33, + 'RTM_GETRULE' => 34, + 'RTM_NEWQDISC' => 36, + 'RTM_DELQDISC' => 37, + 'RTM_GETQDISC' => 38, + 'RTM_NEWTCLASS' => 40, + 'RTM_DELTCLASS' => 41, + 'RTM_GETTCLASS' => 42, + 'RTM_NEWTFILTER' => 44, + 'RTM_DELTFILTER' => 45, + 'RTM_GETTFILTER' => 46, + 'RTM_NEWACTION' => 48, + 'RTM_DELACTION' => 49, + 'RTM_GETACTION' => 50, + 'RTM_NEWPREFIX' => 52, + 'RTM_GETMULTICAST' => 58, + 'RTM_GETANYCAST' => 62, + 'RTM_NEWNEIGHTBL' => 64, + 'RTM_GETNEIGHTBL' => 66, + 'RTM_SETNEIGHTBL' => 67, + 'RTM_NEWNDUSEROPT' => 68, + 'RTM_NEWADDRLABEL' => 72, + 'RTM_DELADDRLABEL' => 73, + 'RTM_GETADDRLABEL' => 74, + 'RTM_GETDCB' => 78, + 'RTM_SETDCB' => 79, + 'RTM_NEWNETCONF' => 80, + 'RTM_GETNETCONF' => 82, + 'RTM_NEWMDB' => 84, + 'RTM_DELMDB' => 85, + 'RTM_GETMDB' => 86, + 'RTM_NEWNSID' => 88, + 'RTM_DELNSID' => 89, + 'RTM_GETNSID' => 90, + 'RTM_NEWSTATS' => 92, + 'RTM_GETSTATS' => 94, + 'RTM_NEWCACHEREPORT' => 96, + 'RTM_NEWCHAIN' => 100, + 'RTM_DELCHAIN' => 101, + 'RTM_GETCHAIN' => 102, + 'RTM_NEWNEXTHOP' => 104, + 'RTM_DELNEXTHOP' => 105, + 'RTM_GETNEXTHOP' => 106, + 'RTM_NEWLINKPROP' => 108, + 'RTM_DELLINKPROP' => 109, + 'RTM_GETLINKPROP' => 110, + 'RTM_NEWVLAN' => 112, + 'RTM_DELVLAN' => 113, + 'RTM_GETVLAN' => 114, + 'RTM_NEWNEXTHOPBUCKET' => 116, + 'RTM_DELNEXTHOPBUCKET' => 117, + 'RTM_GETNEXTHOPBUCKET' => 118, + 'RTM_NEWTUNNEL' => 120, + 'RTM_DELTUNNEL' => 121, + 'RTM_GETTUNNEL' => 122, +); + +# Export all constants +our @EXPORT_OK = keys %constants; +our %EXPORT_TAGS = ( all => \@EXPORT_OK ); + +# Define constants as subroutines +for my $name (@EXPORT_OK) { + no strict 'refs'; + *{$name} = sub () { $constants{$name} }; +} + +1; diff --git a/lib/Sys/Ebpf/Link/Netlink/Socket.pm b/lib/Sys/Ebpf/Link/Netlink/Socket.pm index 2537362..2d51568 100644 --- a/lib/Sys/Ebpf/Link/Netlink/Socket.pm +++ b/lib/Sys/Ebpf/Link/Netlink/Socket.pm @@ -42,8 +42,8 @@ sub new { setsockopt( $self->{sock}, SOL_NETLINK, NETLINK_EXT_ACK, $one ) or warn "Failed to set NETLINK_EXT_ACK: $!"; - # ソケットをバインド - my $sockaddr_nl = pack_sockaddr_nl($$); # プロセスのPIDにバインド + # ソケットをバインド(procss pid bind) + my $sockaddr_nl = pack_sockaddr_nl($$); bind( $self->{sock}, $sockaddr_nl ) or die "Failed to bind Netlink socket: $!"; @@ -81,6 +81,28 @@ sub close { close( $self->{sock} ) if $self->{sock}; } +# helper functions + +sub get_error_message { + my ($response) = @_; + my $offset = NLMSG_HDRLEN + 4; # After error code + my $len = length($response); + + if ( $len > $offset ) { + my $attr_data = substr( $response, $offset ); + while ( length($attr_data) >= NLA_HDRLEN ) { + my ( $nla_len, $nla_type ) = unpack( 'S S', $attr_data ); + my $payload + = substr( $attr_data, NLA_HDRLEN, $nla_len - NLA_HDRLEN ); + if ( $nla_type == 1 ) { # NLMSGERR_ATTR_MSG + return unpack( 'Z*', $payload ); + } + $attr_data = substr( $attr_data, ( $nla_len + 3 ) & ~3 ); + } + } + return ""; +} + # cf. https://github.com/torvalds/linux/blob/3efc57369a0ce8f76bf0804f7e673982384e4ac9/include/uapi/linux/netlink.h#L37 # typedef unsigned short __kernel_sa_family_t; # struct sockaddr_nl { diff --git a/lib/Sys/Ebpf/Link/Netlink/Xdp.pm b/lib/Sys/Ebpf/Link/Netlink/Xdp.pm index 39f48cc..b16162b 100644 --- a/lib/Sys/Ebpf/Link/Netlink/Xdp.pm +++ b/lib/Sys/Ebpf/Link/Netlink/Xdp.pm @@ -2,16 +2,23 @@ package Sys::Ebpf::Link::Netlink::Xdp; use strict; use warnings; + use IO::Interface::Simple (); -use Sys::Ebpf::Link::Netlink::Socket - qw(pack_nlattr pack_nlmsghdr pack_ifinfomsg); +use Socket qw( AF_UNSPEC ); +use Errno (); +use Carp qw( croak ); +use Try::Tiny qw( catch finally try ); + +use Sys::Ebpf::Link::Netlink::Socket qw( + pack_nlattr + pack_nlmsghdr + pack_ifinfomsg +); use Sys::Ebpf::Link::Netlink::Constants::Iflink qw( IFLA_XDP IFLA_XDP_FD - IFLA_XDP_ATTACHED IFLA_XDP_FLAGS XDP_FLAGS_UPDATE_IF_NOEXIST - XDP_FLAGS_SKB_MODE XDP_FLAGS_DRV_MODE ); use Sys::Ebpf::Link::Netlink::Constants::Netlink qw( @@ -22,108 +29,96 @@ use Sys::Ebpf::Link::Netlink::Constants::Netlink qw( NLA_HDRLEN NETLINK_ROUTE NLMSG_ERROR + NETLINK_EXT_ACK ); +use Sys::Ebpf::Link::Netlink::Constants::Rtnetlink qw(RTM_SETLINK); -use Socket qw( AF_UNSPEC ); -use Errno (); +use Data::HexDump (); -# 定数の定義 -use constant { - RTM_SETLINK => 19, - IFF_UP => 1 << 0, -}; +sub _send_and_recv_netlink_message { + my ( $nlmsg, $nlmsg_seq ) = @_; + my $netlink + = Sys::Ebpf::Link::Netlink::Socket->new( Proto => NETLINK_ROUTE ); + try { + $netlink->send_message($nlmsg); -# ヘルパー関数をインポート -use Sys::Ebpf::Link::Netlink::Socket - qw(pack_nlattr pack_nlmsghdr pack_ifinfomsg); + my $response = $netlink->receive_message(); + _handle_response( $response, $nlmsg_seq ); + } + catch { + my $error = $_; + croak $error; + } + finally { + $netlink->close(); + }; +} -sub attach_xdp { - my ( $prog_fd, $ifname ) = @_; +sub _handle_response { + my ( $response, $nlmsg_seq ) = @_; - print "Entering attach_xdp\n"; + my $received_bytes = length($response); - # インターフェースのインデックスを取得 - my $iface = IO::Interface::Simple->new($ifname); - unless ($iface) { - die "Interface $ifname not found"; + if ( $received_bytes >= NLMSG_HDRLEN ) { + my ( $len, $type, $flags, $seq, $pid ) + = unpack( 'I S S I I', substr( $response, 0, NLMSG_HDRLEN ) ); + if ( $seq != $nlmsg_seq ) { + croak "Netlink response sequence number does not match request"; + } + if ( $type == NLMSG_ERROR ) { + my $error_code + = unpack( 'i', substr( $response, NLMSG_HDRLEN, 4 ) ); + if ( $error_code != 0 ) { + my $error_msg + = Sys::Ebpf::Link::Netlink::get_error_message($response); + croak "Netlink error: $error_code ($error_msg)"; + } + } } - my $ifindex = $iface->index; - print "Interface name: $ifname, index: $ifindex\n"; - - # フラグを設定(XDP_FLAGS_SKB_MODEを外す) - my $flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE; - print "XDP flags: $flags\n"; + else { + croak "Received Netlink response is too short"; + } +} - # Netlinkメッセージの構築 - # ifinfomsgをパック - my $ifinfomsg = pack_ifinfomsg( AF_UNSPEC, 0, $ifindex, 0, 0 ); +sub _create_nlmsg { + my ( $ifindex, $prog_fd, $flags ) = @_; - # 属性をパック + my $ifinfomsg = pack( 'C C S L L L', AF_UNSPEC, 0, 0, $ifindex, 0, 0 ); my $nla_fd = pack_nlattr( IFLA_XDP_FD, pack( 'i', $prog_fd ) ); my $nla_flags = pack_nlattr( IFLA_XDP_FLAGS, pack( 'I', $flags ) ); my $xdp_attrs = $nla_fd . $nla_flags; my $nla_xdp = pack_nlattr( NLA_F_NESTED | IFLA_XDP, $xdp_attrs ); - # 全体のメッセージを構築 my $req = $ifinfomsg . $nla_xdp; my $nlmsg_len = NLMSG_HDRLEN + length($req); my $nlmsg_seq = int( rand(0xFFFFFFFF) ); + my $nlmsg = pack_nlmsghdr( $nlmsg_len, RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK, - $nlmsg_seq, $$ ) + $nlmsg_seq, $$, ) . $req; - # Netlinkメッセージのダンプ(デバッグ用) - use Data::HexDump (); - print "Netlink message hex dump:\n"; - print Data::HexDump::HexDump($nlmsg); + return { + nlmsg => $nlmsg, + nlmsg_seq => $nlmsg_seq, + }; +} - # Netlinkソケットを作成してメッセージを送信 - my $netlink - = Sys::Ebpf::Link::Netlink::Socket->new( Proto => NETLINK_ROUTE ); - $netlink->send_message($nlmsg); - print "Netlink message sent successfully\n"; +sub attach_xdp { + my ( $prog_fd, $ifname, $flags ) = @_; - # 応答を受信 - my $response = $netlink->receive_message(); - my $received_bytes = length($response); - print "Received Netlink response, bytes received: $received_bytes\n"; + # インターフェースのインデックスを取得 + my $iface = IO::Interface::Simple->new($ifname); + unless ($iface) { + croak "Interface $ifname not found"; + } + my $ifindex = $iface->index; - # エラーチェック - if ( $received_bytes >= NLMSG_HDRLEN ) { - my ( $len, $type, $flags, $seq, $pid ) - = unpack( 'I S S I I', substr( $response, 0, NLMSG_HDRLEN ) ); - print - "Netlink response header: len=$len, type=$type, flags=$flags, seq=$seq, pid=$pid\n"; - if ( $seq != $nlmsg_seq ) { - die "Netlink response sequence number does not match request"; - } - if ( $type == NLMSG_ERROR ) { - my $error_code - = unpack( 'i', substr( $response, NLMSG_HDRLEN, 4 ) ); - print "Netlink error code: $error_code\n"; - if ( $error_code != 0 ) { + $flags //= XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE; - # 拡張エラーメッセージを取得 - my $error_msg = get_netlink_error_msg($response); - die "Netlink error: $error_code (" - . $error_code - . ") $error_msg"; - } - else { - print "Netlink response indicates success\n"; - } - } - else { - print "Netlink response type: $type\n"; - } - } - else { - print "Received Netlink response is too short\n"; - } + my $nlmsg = _create_nlmsg( $ifindex, $prog_fd, $flags ); - $netlink->close(); - print "Exiting attach_xdp\n"; + _send_and_recv_netlink_message( $nlmsg->{nlmsg}, $nlmsg->{nlmsg_seq} ); return { prog_fd => $prog_fd, @@ -133,98 +128,19 @@ sub attach_xdp { }; } -# 拡張エラーメッセージを取得する関数 -sub get_netlink_error_msg { - my ($response) = @_; - my $offset = NLMSG_HDRLEN + 4; # After the error code - my $len = length($response); - if ( $len > $offset ) { - my $attr_data = substr( $response, $offset ); - while ( length($attr_data) >= NLA_HDRLEN ) { - my ( $nla_len, $nla_type ) = unpack( "S S", $attr_data ); - my $payload - = substr( $attr_data, NLA_HDRLEN, $nla_len - NLA_HDRLEN ); - if ( $nla_type == 1 ) { # NLMSGERR_ATTR_MSG - return unpack( "Z*", $payload ); - } - $attr_data = substr( $attr_data, ( $nla_len + 3 ) & ~3 ) - ; # Align to 4 bytes - } - } - return ""; -} - sub detach_xdp { - my ($ifname) = @_; - - print "Entering detach_xdp\n"; + my ( $ifname, $flags ) = @_; my $iface = IO::Interface::Simple->new($ifname); unless ($iface) { - die "Interface $ifname not found"; + croak "Interface $ifname not found"; } my $ifindex = $iface->index; - print "Detaching XDP from interface: $ifname (index: $ifindex)\n"; - - # Netlinkメッセージの構築 - # ifinfomsgをパック - my $ifinfomsg = pack_ifinfomsg( AF_UNSPEC, 0, $ifindex, 0, 0 ); + $flags //= XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE; - # 属性をパック - my $prog_fd = -1; - my $nla_fd = pack_nlattr( IFLA_XDP_FD, pack( 'i', $prog_fd ) ); - my $flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE; - my $nla_flags = pack_nlattr( IFLA_XDP_FLAGS, pack( 'I', $flags ) ); - my $xdp_attrs = $nla_fd . $nla_flags; - my $nla_xdp = pack_nlattr( NLA_F_NESTED | IFLA_XDP, $xdp_attrs ); - - # 全体のメッセージを構築 - my $req = $ifinfomsg . $nla_xdp; - my $nlmsg_len = NLMSG_HDRLEN + length($req); - my $nlmsg - = pack_nlmsghdr( $nlmsg_len, RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK, - 0, $$ ) - . $req; - - # Netlinkソケットを作成してメッセージを送信 - my $netlink - = Sys::Ebpf::Link::Netlink::Socket->new( Proto => NETLINK_ROUTE ); - $netlink->send_message($nlmsg); - print "Netlink message sent successfully\n"; - - # 応答を受信 - my $response = $netlink->receive_message(); - my $received_bytes = length($response); - print "Received Netlink response, bytes received: $received_bytes\n"; - - # エラーチェック - if ( $received_bytes >= NLMSG_HDRLEN ) { - my ( $len, $type, $flags, $seq, $pid ) - = unpack( 'I S S I I', substr( $response, 0, NLMSG_HDRLEN ) ); - print - "Netlink response header: len=$len, type=$type, flags=$flags, seq=$seq, pid=$pid\n"; - if ( $type == NLMSG_ERROR ) { - my $error_code - = unpack( 'i', substr( $response, NLMSG_HDRLEN, 4 ) ); - print "Netlink error code: $error_code\n"; - if ( $error_code != 0 ) { - die "Netlink error during detach: $error_code (" - . strerror( -$error_code ) . ")"; - } - else { - print "Netlink response indicates success\n"; - } - } - else { - print "Netlink response type: $type\n"; - } - } - else { - print "Received Netlink response is too short\n"; - } + my $nlmsg = _create_nlmsg( $ifindex, -1, $flags ); - $netlink->close(); - print "Exiting detach_xdp\n"; + _send_and_recv_netlink_message( $nlmsg->{nlmsg}, $nlmsg->{nlmsg_seq} ); } 1;