diff --git a/apps/sdlan/src/network/arp_packet.erl b/apps/sdlan/src/network/arp_packet.erl new file mode 100644 index 0000000..0a22300 --- /dev/null +++ b/apps/sdlan/src/network/arp_packet.erl @@ -0,0 +1,153 @@ +%%%------------------------------------------------------------------- +%%% @doc ARP packet parser / builder (Ethernet + IPv4) +%%%------------------------------------------------------------------- +-module(arp_packet). + +-export([parse/1, marshal/1, is_broadcast_mac/1, arp_request/3, arp_response/3]). + +-define(ARP_REQUEST, 1). +-define(ARP_RESPONSE, 2). + +-define(ETHERNET, 16#0001). +-define(IPV4, 16#0800). + +-define(BROADCAST_MAC, <<255,255,255,255,255,255>>). + +-record(arp_packet, { + hardware_type, + protocol_type, + hardware_size, + protocol_size, + opcode, + sender_mac, + sender_ip, + target_mac, + target_ip +}). + +%%-------------------------------------------------------------------- +%% Parsing +%%-------------------------------------------------------------------- + +-spec parse(binary()) -> + {ok, #arp_packet{}} | {error, invalid_length | invalid_opcode}. +parse(Bin) when is_binary(Bin) -> + case Bin of + << + HType:16/big, + PType:16/big, + HLen:8, + PLen:8, + Op:16/big, + SMac:6/binary, + SIP:32/big, + TMac:6/binary, + TIP:32/big, + _/binary + >> -> + case decode_opcode(Op) of + {ok, Opcode} -> + {ok, #arp_packet{ + hardware_type = HType, + protocol_type = PType, + hardware_size = HLen, + protocol_size = PLen, + opcode = Opcode, + sender_mac = SMac, + sender_ip = SIP, + target_mac = TMac, + target_ip = TIP + }}; + error -> + {error, invalid_opcode} + end; + _ -> + {error, invalid_length} + end. + +%%-------------------------------------------------------------------- +%% Marshal +%%-------------------------------------------------------------------- + +-spec marshal(#arp_packet{}) -> binary(). +marshal(#arp_packet{ + hardware_type = HType, + protocol_type = PType, + hardware_size = HLen, + protocol_size = PLen, + opcode = Opcode, + sender_mac = SMac, + sender_ip = SIP, + target_mac = TMac, + target_ip = TIP +}) -> + Op = encode_opcode(Opcode), + << + HType:16/big, + PType:16/big, + HLen:8, + PLen:8, + Op:16/big, + SMac/binary, + SIP:32/big, + TMac/binary, + TIP:32/big + >>. + +%%-------------------------------------------------------------------- +%% Opcode helpers +%%-------------------------------------------------------------------- + +decode_opcode(?ARP_REQUEST) -> {ok, request}; +decode_opcode(?ARP_RESPONSE) -> {ok, response}; +decode_opcode(_) -> error. + +encode_opcode(request) -> ?ARP_REQUEST; +encode_opcode(response) -> ?ARP_RESPONSE. + +%%-------------------------------------------------------------------- +%% Utils +%%-------------------------------------------------------------------- + +-spec is_broadcast_mac(binary()) -> boolean(). +is_broadcast_mac(?BROADCAST_MAC) -> true; +is_broadcast_mac(_) -> false. + +%%-------------------------------------------------------------------- +%% Builders (对齐 Swift 扩展) +%%-------------------------------------------------------------------- + +-spec arp_request(integer(), binary(), integer()) -> #arp_packet{}. +arp_request(SenderIP, SenderMAC, TargetIP) -> + #arp_packet{ + hardware_type = ?ETHERNET, + protocol_type = ?IPV4, + hardware_size = 6, + protocol_size = 4, + opcode = request, + sender_mac = SenderMAC, + sender_ip = SenderIP, + target_mac = <<0,0,0,0,0,0>>, + target_ip = TargetIP + }. + +-spec arp_response(#arp_packet{}, binary(), integer()) -> #arp_packet{}. +arp_response(#arp_packet{ + hardware_type = HType, + protocol_type = PType, + hardware_size = HLen, + protocol_size = PLen, + sender_mac = ReqMac, + sender_ip = ReqIP +}, Mac, IP) -> + #arp_packet{ + hardware_type = HType, + protocol_type = PType, + hardware_size = HLen, + protocol_size = PLen, + opcode = response, + sender_mac = Mac, + sender_ip = IP, + target_mac = ReqMac, + target_ip = ReqIP + }. \ No newline at end of file diff --git a/apps/sdlan/src/network/layer_packet.erl b/apps/sdlan/src/network/layer_packet.erl new file mode 100644 index 0000000..c3e73bc --- /dev/null +++ b/apps/sdlan/src/network/layer_packet.erl @@ -0,0 +1,88 @@ +%%%------------------------------------------------------------------- +%%% @doc Ethernet II layer packet parser +%%%------------------------------------------------------------------- +-module(layer_packet). + +-export([parse/1, marshal/1, is_broadcast_mac/1, is_multicast_mac/1]). + +-define(ETH_ARP, 16#0806). +-define(ETH_IPV4, 16#0800). +-define(ETH_IPV6, 16#86DD). +-define(ETH_VLAN, 16#8100). + +-record(layer_packet, { + dst_mac, + src_mac, + type, + payload +}). + +%%-------------------------------------------------------------------- +%% API +%%-------------------------------------------------------------------- + +-spec parse(binary()) -> {ok, #layer_packet{}} | {error, invalid_length | invalid_type}. +parse(Bin) when is_binary(Bin) -> + case Bin of + <> -> + case decode_type(Type) of + {ok, T} -> + {ok, #layer_packet{ + dst_mac = Dst, + src_mac = Src, + type = T, + payload = Payload + }}; + error -> + {error, invalid_type} + end; + _ -> + {error, invalid_length} + end. + +%%-------------------------------------------------------------------- + +-spec marshal(#layer_packet{}) -> binary(). +marshal(#layer_packet{dst_mac = Dst, src_mac = Src, type = Type, payload = Payload}) -> + TypeBin = encode_type(Type), + <>. + +%%-------------------------------------------------------------------- +%% EtherType mapping +%%-------------------------------------------------------------------- + +decode_type(?ETH_ARP) -> + {ok, arp}; +decode_type(?ETH_IPV4) -> + {ok, ipv4}; +decode_type(?ETH_IPV6) -> + {ok, ipv6}; +decode_type(?ETH_VLAN) -> + {ok, tagged_frame}; +decode_type(_) -> + error. + +encode_type(arp) -> + ?ETH_ARP; +encode_type(ipv4) -> + ?ETH_IPV4; +encode_type(ipv6) -> + ?ETH_IPV6; +encode_type(tagged_frame) -> + ?ETH_VLAN. + +%%-------------------------------------------------------------------- +%% MAC helpers (对齐 Swift MacAddress) +%%-------------------------------------------------------------------- + +-spec is_broadcast_mac(binary()) -> boolean(). +is_broadcast_mac(<<255,255,255,255,255,255>>) -> + true; +is_broadcast_mac(_) -> + false. + +-spec is_multicast_mac(binary()) -> boolean(). +is_multicast_mac(<<1,0,94,_/binary>>) -> + true; +is_multicast_mac(_) -> + false. \ No newline at end of file