• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

恥ずかしい勘違いから生まれた、DHCP6の不要かつ部分的な実装


Commit MetaInfo

Revision3a2139426df753d37656236297caa3d9e00dc28c (tree)
Zeit2021-08-11 04:20:49
Autordyknon <dyknon@user...>
Commiterdyknon

Log Message

sending packet OK.

Ändern Zusammenfassung

Diff

--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
1+test:
2+ sudo perl test.pl
--- a/Net/DHCP6/Message.pm
+++ b/Net/DHCP6/Message.pm
@@ -4,17 +4,21 @@ package Net::DHCP6::Message;
44 use strict;
55 use warnings;
66
7-our $VERSION = "0.0.1"
7+our $VERSION = "0.0.1";
88
99 # TODO: should be crypto-safe RNG?
1010 our $RNG = sub{ int rand shift };
1111
12+sub gen_xid {
13+ $RNG->(2**24);
14+}
15+
1216 sub new {
1317 my $class = shift;
1418 my $type = shift;
1519 my $id = ref $_[0] ? undef : shift;
1620 unless(defined $id){
17- $id = $RNG->(2**24);
21+ $id = $class->gen_xid;
1822 }
1923 my $self = {
2024 type => $type,
@@ -88,3 +92,15 @@ sub parse {
8892 }
8993 $class->new($type, $xid, @option);
9094 }
95+
96+use overload (
97+ '""' => sub {
98+ my $self = shift;
99+ my $name = "Message" . $self->type;
100+ "($name " . sprintf("xid=0x%06x", $self->xid)
101+ . join("", map{" $_"} $self->options)
102+ . ")";
103+ }
104+);
105+
106+1;
--- /dev/null
+++ b/Net/DHCP6/MessageExchange.pm
@@ -0,0 +1,235 @@
1+package Net::DHCP6::MessageExchange;
2+# RFC8415 15.
3+
4+use strict;
5+use warnings;
6+
7+use Exporter;
8+use Net::DHCP6::Message;
9+use Net::DHCP6::Parameters qw(
10+ DHCP6_ADDR_ALL_SERVERS
11+ DHCP6_PORT_CLIENT
12+ DHCP6_PORT_SERVER
13+);
14+use Socket qw/getaddrinfo AF_INET6 SOCK_DGRAM AI_PASSIVE/;
15+use POSIX qw/EAGAIN EWOULDBLOCK/;
16+use Time::HiRes;
17+use IO::Select;
18+
19+our @ISA = qw/Exporter/;
20+our @EXPORT_OK = qw/NEXT LAST/;
21+our $VERSION = "0.0.1";
22+our $bufsize = 2 ** 16;
23+
24+sub get_default_local_address {
25+ my @ai = getaddrinfo(undef, DHCP6_PORT_CLIENT, {
26+ flags => AI_PASSIVE,
27+ family => AF_INET6,
28+ socktype => SOCK_DGRAM,
29+ });
30+ die $ai[0] if("$ai[0]");
31+ $ai[1]->{addr};
32+}
33+sub get_default_peer_address {
34+ my @ai = getaddrinfo(DHCP6_ADDR_ALL_SERVERS, DHCP6_PORT_SERVER, {
35+ flags => 0,
36+ family => AF_INET6,
37+ socktype => SOCK_DGRAM,
38+ });
39+ die $ai[0] if("$ai[0]");
40+ $ai[1]->{addr};
41+}
42+
43+use constant NEXT => 0;
44+use constant LAST => 1;
45+
46+sub new {
47+ my $class = shift;
48+ my %args = @_;
49+ if(!exists $args{sock}){
50+ $args{sock} = IO::Socket::IP->new(
51+ Family => AF_INET6,
52+ Type => SOCK_DGRAM,
53+ Blocking => 0,
54+ ) or die "error while creating socket: $@";
55+ $args{local} //= undef;
56+ }
57+ if(exists $args{local}){
58+ $args{local} //= get_default_local_address;
59+ $args{sock}->bind($args{local}) or die "error while binding: $!";
60+ delete $args{local};
61+ }
62+ my $self = {
63+ %args,
64+ xid => $args{xid} // Net::DHCP6::Message::gen_xid,
65+ peer => $args{peer} // get_default_peer_address,
66+ start => undef,
67+ rt => undef,
68+ sumrt => 0,
69+ rc => 0,
70+ };
71+ bless $self, $class;
72+}
73+
74+sub irt { ... }
75+sub mrc { ... }
76+sub mrt { ... }
77+sub mrd { ... }
78+sub failed { ... }
79+sub recved { ... }
80+
81+sub now {
82+ my $self = shift;
83+ Time::HiRes::time;
84+}
85+sub sock {
86+ my $self = shift;
87+ $self->{sock};
88+}
89+sub xid {
90+ my $self = shift;
91+ $self->{xid};
92+}
93+sub peer {
94+ my $self = shift;
95+ $self->{peer};
96+}
97+sub start {
98+ my $self = shift;
99+ $self->{start};
100+}
101+sub elapsed {
102+ my $self = shift;
103+ return undef if(!defined $self->start);
104+ $self->now() - $self->start();
105+}
106+sub rt {
107+ my $self = shift;
108+ $self->{rt};
109+}
110+sub rc {
111+ my $self = shift;
112+ $self->{rc};
113+}
114+sub rand {
115+ my $self = shift;
116+ rand(0.2) - 0.1;
117+}
118+sub sumrt {
119+ my $self = shift;
120+ $self->{sumrt};
121+}
122+sub timer_req {
123+ my $self = shift;
124+ my $diff = $self->sumrt() - $self->elapsed();
125+ $diff < 0 ? 0 : $diff;
126+}
127+
128+sub next_rt {
129+ my $self = shift;
130+ my $rt_prev = $self->rt;
131+ my $rt;
132+ if(defined $rt_prev){
133+ $rt = 2 * $rt_prev + $self->rand() * $rt_prev;
134+ }else{
135+ $rt = $self->irt() + $self->rand() * $self->irt();
136+ }
137+ if($self->mrt && $rt > $self->mrt){
138+ $rt = $self->mrt() + $self->rand() * $self->mrt();
139+ }
140+ $self->{rt} = $rt;
141+}
142+
143+sub next_sumrt {
144+ my $self = shift;
145+ if(
146+ $self->mrd && $self->{sumrt} > $self->mrd ||
147+ $self->mrc && $self->{rc}++ > $self->mrc
148+ ){
149+ $self->{sumrt} = undef;
150+ return undef;
151+ }
152+ $self->{sumrt} += $self->next_rt;
153+ if($self->mrd && $self->{sumrt} > $self->mrd){
154+ $self->{sumrt} = $self->mrd;
155+ }
156+ $self->{sumrt};
157+}
158+
159+sub send {
160+ my $self = shift;
161+ my $mes = $self->message;
162+ $self->sock->send($mes->serialize, 0, $self->peer)
163+ or die "sending error: $!";
164+}
165+
166+sub next {
167+ my $self = shift;
168+ if(defined $self->start){
169+ my $recved = $self->recv;
170+ goto EXIT if(!$recved);
171+ goto EXIT if($recved->xid != $self->xid);
172+ if($self->recved($recved) == LAST){
173+ return LAST;
174+ }
175+
176+EXIT:
177+ if($self->elapsed >= $self->sumrt){
178+ if(!defined $self->next_sumrt){
179+ $self->failed;
180+ return LAST;
181+ }
182+ $self->send;
183+ }
184+ return NEXT;
185+ }else{
186+ $self->flush_recv;
187+ $self->send;
188+ $self->{start} = $self->now;
189+ $self->next_sumrt;
190+ return NEXT;
191+ }
192+}
193+
194+sub recv {
195+ my $self = shift;
196+ my $buf;
197+ my $from = $self->sock->recv($buf, $bufsize, 0);
198+ if(!defined $from){
199+ if($! == EAGAIN || $! == EWOULDBLOCK){
200+ return undef;
201+ }else{
202+ die "socket io error: $!";
203+ }
204+ }
205+ my $parsed = eval { Net::DHCP6::Message->parse($buf); };
206+ if(wantarray){
207+ ($parsed, $from);
208+ }else{
209+ $parsed;
210+ }
211+}
212+
213+sub flush_recv {
214+ my $self = shift;
215+ while(1){
216+ my $buf;
217+ my $from = $self->sock->recv($buf, 1, 0);
218+ if(!defined $from){
219+ if($! == EAGAIN || $! == EWOULDBLOCK){
220+ last;
221+ }else{
222+ die "socket io error";
223+ }
224+ }
225+ last if(length $buf == 0);
226+ }
227+}
228+
229+sub loop {
230+ my $self = shift;
231+ my $select = IO::Select->new($self->sock);
232+ while($self->next == NEXT){
233+ $select->can_read($self->timer_req);
234+ }
235+}
--- a/Net/DHCP6/Option.pm
+++ b/Net/DHCP6/Option.pm
@@ -4,7 +4,7 @@ package Net::DHCP6::Option;
44 use strict;
55 use warnings;
66
7-our $VERSION = "0.0.1"
7+our $VERSION = "0.0.1";
88 our %OptionPkgs;
99
1010 # should overridden
@@ -57,12 +57,12 @@ sub parse {
5757
5858 sub register_option {
5959 my $class = shift;
60- $OptionPkgs{$class->code} = $class;
60+ $OptionPkgs{+$class->code} = $class;
6161 }
6262
6363 sub find_subclass {
6464 my $class = shift;
65- my $code = shift;
65+ my $code = +shift;
6666
6767 if($class eq __PACKAGE__ && exists $OptionPkgs{$code}){
6868 $OptionPkgs{$code};
@@ -81,16 +81,30 @@ sub import {
8181 my ($full, $snake);
8282 if($import eq "raw"){
8383 $full = "Net::DHCP6::Option";
84- $snake = "raw";
84+ $snake = "";
8585 }else{
8686 require "Net/DHCP6/Option/$import.pm";
8787 $full = "Net::DHCP6::Option::$import";
8888 $snake = $import;
8989 $snake =~ s/[^A-Z_]\K(?=[A-Z]+)/_/g;
9090 $snake = lc $snake;
91+ $snake = "_" . $snake;
92+ }
93+ {
94+ no strict "refs";
95+ *{"${into}::d6opt_new${snake}"} = sub{ $full->new(@_); };
9196 }
92- *{"${into}::d6opt_new_${snake}"} = sub{ $full->new(@_); };
9397 }
9498 }
9599
100+use overload (
101+ '""' => sub {
102+ my $self = shift;
103+ my $name = $self->name // "Option$self->code";
104+ "($name 0x"
105+ . join("", map{sprintf("%02x", $_)}unpack("C*", $self->data))
106+ . ")";
107+ }
108+);
109+
96110 1;
--- a/Net/DHCP6/Option/ElapsedTime.pm
+++ b/Net/DHCP6/Option/ElapsedTime.pm
@@ -6,13 +6,20 @@ use warnings;
66 use Net::DHCP6::SimpleOption;
77 use Net::DHCP6::Parameters qw/DHCP6_OPT_ELAPSED_TIME/;
88
9-our $VERSION = "0.0.1"
9+our $VERSION = "0.0.1";
1010 our @ISA = qw/Net::DHCP6::SimpleOption/;
1111
1212 use constant code => DHCP6_OPT_ELAPSED_TIME;
1313 use constant name => "ElapsedTime";
1414 use constant len => 2;
1515
16+sub new_special {
17+ my $class = shift;
18+ my $val = shift;
19+ my $self = ref $val ? $val : \int($val * 100);
20+ bless $self, $class;
21+}
22+
1623 sub data {
1724 my $self = shift;
1825 die "out of range" unless($self->time >= 0 && $self->time < 2**16);
--- a/Net/DHCP6/Option/OptionRequest.pm
+++ b/Net/DHCP6/Option/OptionRequest.pm
@@ -6,7 +6,7 @@ use warnings;
66 use Net::DHCP6::Option;
77 use Net::DHCP6::Parameters qw/DHCP6_OPT_ORO/;
88
9-our $VERSION = "0.0.1"
9+our $VERSION = "0.0.1";
1010 our @ISA = qw/Net::DHCP6::Option/;
1111
1212 use constant code => DHCP6_OPT_ORO;
@@ -35,6 +35,7 @@ sub len {
3535
3636 sub data {
3737 my $self = shift;
38+ die "out of range" if(join "", map{$_ < 0 || $_ >= 2**16} @$self);
3839 pack("S>*", @$self);
3940 }
4041
--- a/Net/DHCP6/Parameters.pm
+++ b/Net/DHCP6/Parameters.pm
@@ -4,7 +4,7 @@ package Net::DHCP6::Parameters;
44 use strict;
55 use warnings;
66
7-our $VERSION = "0.0.1"
7+our $VERSION = "0.0.1";
88
99 use Exporter;
1010 our @ISA = ("Exporter");
@@ -12,7 +12,18 @@ our @ISA = ("Exporter");
1212 our @EXPORT_OK;
1313 our %EXPORT_TAGS;
1414
15-our %MessageType = (
15+# RFC8415 7.
16+use constant DHCP6_NETWORK_CONSTANTS => (
17+ DHCP6_ADDR_ALL_SERVERS => "ff05::1:2",
18+ DHCP6_ADDR_ALL_ONLY_SERVERS => "ff05::1:3",
19+ DHCP6_PORT_CLIENT => 546,
20+ DHCP6_PORT_SERVER => 547,
21+);
22+use constant {DHCP6_NETWORK_CONSTANTS};
23+push @EXPORT_OK, keys %{{DHCP6_NETWORK_CONSTANTS}};
24+$EXPORT_TAGS{NetworkConstant} = [keys %{{DHCP6_NETWORK_CONSTANTS}}];
25+
26+use constant DHCP6_MESSAGE_TYPES => (
1627 DHCP6_MT_SOLICIT => 1,
1728 DHCP6_MT_ADVERTISE => 2,
1829 DHCP6_MT_REQUEST => 3,
@@ -49,11 +60,11 @@ our %MessageType = (
4960 DHCP6_MT_STATE => 34,
5061 DHCP6_MT_CONTACT => 35,
5162 );
52-use constant \%MessageType;
53-push @EXPORT_OK, keys %MessageType;
54-$EXPORT_TAGS{MessageType} = [keys %MessageType];
63+use constant {DHCP6_MESSAGE_TYPES};
64+push @EXPORT_OK, keys %{{DHCP6_MESSAGE_TYPES}};
65+$EXPORT_TAGS{MessageType} = [keys %{{DHCP6_MESSAGE_TYPES}}];
5566
56-our %OptionCode = (
67+use constant DHCP6_OPTION_CODES => (
5768 DHCP6_OPT_CLIENTID => 1,
5869 DHCP6_OPT_SERVERID => 2,
5970 DHCP6_OPT_IA_NA => 3,
@@ -196,11 +207,11 @@ our %OptionCode = (
196207 DHCP6_OPT_V6_DOTS_ADDRESS => 142,
197208 DHCP6_OPT_IPv6_ADDRESS_ANDSF => 143,
198209 );
199-use constant \%OptionCode;
200-push @EXPORT_OK, keys %OptionCode;
201-$EXPORT_TAGS{OptionCode} = [keys %OptionCode];
210+use constant {DHCP6_OPTION_CODES};
211+push @EXPORT_OK, keys %{{DHCP6_OPTION_CODES}};
212+$EXPORT_TAGS{OptionCode} = [keys %{{DHCP6_OPTION_CODES}}];
202213
203-our %StatusCode = (
214+use constant DHCP6_STATUS_CODES => (
204215 DHCP_ST_SUCCESS => 0,
205216 DHCP_ST_UNSPEC_FAIL => 1,
206217 DHCP_ST_NO_ADDRS_AVAIL => 2,
@@ -225,8 +236,8 @@ our %StatusCode = (
225236 DHCP_ST_DNS_UPDATE_NOT_SUPPORTED => 21,
226237 DHCP_ST_EXCESSIVE_TIME_SKEW => 22,
227238 );
228-use constant \%StatusCode;
229-push @EXPORT_OK, keys %StatusCode;
230-$EXPORT_TAGS{StatusCode} = [keys %StatusCode];
239+use constant {DHCP6_STATUS_CODES};
240+push @EXPORT_OK, keys %{{DHCP6_STATUS_CODES}};
241+$EXPORT_TAGS{StatusCode} = [keys %{{DHCP6_STATUS_CODES}}];
231242
232243 1;
--- a/Net/DHCP6/SimpleOption.pm
+++ b/Net/DHCP6/SimpleOption.pm
@@ -4,7 +4,7 @@ use strict;
44 use warnings;
55 use Net::DHCP6::Option;
66
7-our $VERSION = "0.0.1"
7+our $VERSION = "0.0.1";
88 our @ISA = qw/Net::DHCP6::Option/;
99
1010 sub new {
--- /dev/null
+++ b/Net/DHCP6/Stateless.pm
@@ -0,0 +1,83 @@
1+package Net::DHCP6::Stateless;
2+# RFC8415 6.1.
3+# RFC8415 18.2.6.
4+
5+use strict;
6+use warnings;
7+
8+use Net::DHCP6::MessageExchange qw/NEXT LAST/;
9+
10+use Net::DHCP6::Parameters qw(
11+ DHCP6_MT_REPLY
12+ DHCP6_MT_INFORMATION_REQUEST
13+ DHCP6_OPT_INF_MAX_RT
14+ DHCP6_OPT_INFORMATION_REFRESH_TIME
15+);
16+use Net::DHCP6::Message;
17+use Net::DHCP6::Option qw/ElapsedTime OptionRequest/;
18+
19+our $VERSION = "0.0.1";
20+our @ISA = qw/Net::DHCP6::MessageExchange/;
21+
22+use constant irt => 1;
23+use constant mrc => 0;
24+use constant mrd => 0;
25+
26+sub add_elements_if_not_exists {
27+ my $array = shift;
28+ for my $elem(@_){
29+ if(!grep{$_ == $elem} @$array){
30+ push @$array, $elem;
31+ }
32+ }
33+}
34+
35+sub new {
36+ my $class = shift;
37+ my %opts = @_;
38+ die "requesting options are not given" if(ref $opts{request} ne "ARRAY");
39+
40+ $opts{mrt} //= 3600;
41+ if($opts{auto_request} // 1){
42+ add_elements_if_not_exists($opts{request},
43+ DHCP6_OPT_INF_MAX_RT,
44+ DHCP6_OPT_INFORMATION_REFRESH_TIME
45+ );
46+ }
47+ delete $opts{auto_request};
48+ $opts{failed} //= sub{};
49+ $opts{recved} //= sub{ 1; };
50+
51+ Net::DHCP6::MessageExchange::new($class, %opts);
52+}
53+
54+sub mrt {
55+ my $self = shift;
56+ $self->{mrt};
57+}
58+
59+sub failed {
60+ my $self = shift;
61+ $self->{failed}->();
62+}
63+
64+sub recved {
65+ my $self = shift;
66+ my $recved = shift;
67+ return NEXT if($recved->type != DHCP6_MT_REPLY);
68+ $self->{recved}->($recved) ? LAST : NEXT;
69+}
70+
71+sub requests {
72+ my $self = shift;
73+ @{$self->{request}}
74+}
75+
76+sub message {
77+ my $self = shift;
78+ Net::DHCP6::Message->new(DHCP6_MT_INFORMATION_REQUEST,
79+ d6opt_new_option_request($self->requests),
80+ d6opt_new_elapsed_time($self->elapsed // 0),
81+ );
82+}
83+
--- /dev/null
+++ b/test.pl
@@ -0,0 +1,22 @@
1+#!/bin/perl
2+
3+use strict;
4+use warnings;
5+use lib ".";
6+
7+use IO::Socket::IP;
8+use Data::Dumper;
9+
10+use Net::DHCP6::Parameters qw(
11+ DHCP6_OPT_DNS_SERVERS
12+);
13+use Net::DHCP6::Stateless;
14+
15+my $client = Net::DHCP6::Stateless->new(
16+ request => [],
17+ failed => sub{print("timeout\n");},
18+ recved => sub{print(shift."\n"); 1;},
19+);
20+
21+local $| = 1;
22+$client->loop;