ToDo:
IPv6サポートのためにFirewallの再構築を実施
VLANも増えてきたので、DMZおよび境界制御ルールの記述パターンの標準化と 動的ルールの全面的な導入・tableおよびtagアクションオプションによる記述の圧縮などを実施
得られた知見をメモ
境界ルーター上では、IPv6通信がつかえるようになった
実用レベルでIPv4 over IPv6を使うには、プロバイダー側がMAP-Eなのv4 NATの実装が面倒だし、外部からのIPv4着信を考えるとIPv4 PPPoE接続も維持して、用途によってIPv4経路を選択する必要があるので、routing tableの多重化を行い接続経路やjail/socketにFIBを設定する必要がある
WAN側のIPv6通信は確保したので、LAN側をどうするかの考察
技術的には以下のアイデアがある
一番手がかからないのはYAMAHAルーターあたりを買ってくる(ついでにMAP-EなIPv4 over IPv6トンネルも開通する)辺りだが、LAN側にDNSで引けるIPv6アドレスを割り当てるなら次点はunique local unicastによる実装か?
久々にカーネルパニック(Fatal trap 18: integer divide fault while in kernel mode)で死んだ
バックトレースからsys/netinet/libalias/alias_db.cが怪しい
KDB: stack backtrace: db_trace_self_wrapper() at db_trace_self_wrapper+0x2b/frame 0xfffffe00b27f30b0 vpanic() at vpanic+0x181/frame 0xfffffe00b27f3100 panic() at panic+0x43/frame 0xfffffe00b27f3160 trap_fatal() at trap_fatal+0x387/frame 0xfffffe00b27f31c0 trap() at trap+0x8b/frame 0xfffffe00b27f32d0 calltrap() at calltrap+0x8/frame 0xfffffe00b27f32d0 --- trap 0x12, rip = 0xffffffff808a68bc, rsp = 0xfffffe00b27f33a0, rbp = 0xfffffe00b27f33b0 --- HouseKeeping() at HouseKeeping+0x1c/frame 0xfffffe00b27f33b0 LibAliasInLocked() at LibAliasInLocked+0x2f/frame 0xfffffe00b27f3470
おそらく当該コードは stable/13 390866d47effe8f5a11f3f852ae891f14dd4d15cで導入されたHouseKeepingの起床レートを処理パケットレートに基づいて調整するコードのコーナーケースと思われる
コーディング的には、kernel modeでの経時処理が重いので可能な限り処理パケット数で起床レートを制御し、直近約1秒の処理パケット数から平均毎秒3回の起床レートとなるようpacket_limitを制御するのが目的だが、パケットレートが低い場合に穴がある模様
mainも現時点 では、治ってないぽぃ
修正は、packet_limitに下限値を設定しておく辺りか
user spaceのnatdを使っている場合は、デーモンが死んでNATが機能しなくなるものと思われる
void HouseKeeping(struct libalias *la) { static unsigned int packets = 0; static unsigned int packet_limit = 1000; LIBALIAS_LOCK_ASSERT(la); packets++; /* * User space time/gettimeofday/... is very expensive. * Kernel space cache trashing is unnecessary. * * Save system time (seconds) in global variable LibAliasTime * for use by other functions. This is done so as not to * unnecessarily waste timeline by making system calls. * * Reduce the amount of house keeping work substantially by * sampling over the packets. */ if (packets % packet_limit == 0) { time_t now; #ifdef _KERNEL now = time_uptime; #else now = time(NULL); #endif if (now != LibAliasTime) { /* retry three times a second */ packet_limit = packets / 3; packets = 0; LibAliasTime = now; } } /* Do a cleanup for the first packets of the new second only */ if (packets < (la->udpLinkCount + la->tcpLinkCount)) { struct alias_link * lnk = TAILQ_FIRST(&la->checkExpire); CleanupLink(la, &lnk, 0); } }
一応、sort_utils.hとseq_utils.hを使ったIntersection/Complement実装が完了
これで、Sort/Union/Intersection/Complementに関しては、SADインタプリタスタックに依存せずに処理可能になったはず
とりあえず、基本機能は実装完了
実装途中で整理が必要なのは
実用上必要なのが、
実用上、Join/Map関連が再実装できると、長いリストが気軽に扱える用になってGood
実装の整理を目的にAPIよstring-buffer構造体の中身と動作の調査を開始…
コンソール向けに出力幅制御関連の妙な仕掛けが多く、関連コードが散らかっている・パラメータがilist配列のインデックスのみで無名なので、意味論の解読に骨が折れる Orz
一部のイケてない実装 (arguemnt数確認前にstackを読み出すおバカさん)のために、関数コール前に zero arguement時に Nullを積み込んでいる実装の副作用で引数エラー回りの処理が面倒になっているので解決したい
アイデア段階だけど、次のような手順で直せばいいのでは?
とりあえず、NPTv6の予備的な試験でipfwのcrashを見つけた int_prefixに/nなprefixlen付き表記を使うと落ちる
ipfw nptv6 foo create ext_if em0 int_prefix ####::/64
int_prefixにprefixlenを含めず、別途prefixlenパラメータを指定するのはOK
sbin/ipfw/nptv6.cのnptv6_createのバグで、nptv6_parse_prefixでデコードした結果にprefixlenが含まれる際にgoto check_prefix;でprefixlenの範囲を検査するコードに委譲するのだが、先方はprefixlenオプション直後のchar pointer pの指す内容も検査しているため、未初期化ポインタによる参照でcrashする
とりあえず、check_prefixラベルの前後にポインタ検査とplenの検査を分離して解決
やはり、main(14-CURRENT)も治ってないぽぃ
あまり使ったという話が流れてないので、誰も使ってないのかなぁ・・・
IPv6 LAN向けの検証作業
とりあえずまとめ
rtadvd'およびDHCPv6 serverを立ち上げて、LAN側からIPv6アドレスを取得出来るところまでは確立
だが、外に出れない Orz
LAN側からWAN側にNTPv6でprefix変換して送出したパケットが戻ってこれない状況
原因は、回線側のルーターからNTPv6でprefix変換されてWAN側アドレスに対して近傍探索が届くが、WAN側のインターフェースに存在しないアドレスなので、応答しないため、回線側からWAN側インターフェースへの通信が開始されない模様
素直にNAT66で繋ごうにも、ipfwにNAT66は未実装な罠 Orz (pfは実装済みぽぃ)
回線側のサポートがあれば、DHCPv6-PDでWAN側からprefix delegationをもらってLAN側prefixを設定できるが、光電話契約してないので無理 (KAMEの影響か、BSD系の設定例はWIDE DHCP clientを使ったものが多い様だ)
回線契約形態で、prefix delegationの振り出し状態が変わるということは、ND動作を考えると、回線側のルータ設定も連動して変更されていて、prefix block単位でroutingされているのかなぁ…
NAT66のためだけにipfwからpfへ切り替えるのも面倒くさそう…(YAMAHAルーター買ってくるか?)
まあ、socket pid/gid filteringは止めたのでipfwでないと困る理由もなくなったのだが
DHCPやlink localな通信を先に許可した後、残りの通信に対してLAN側に割り当て済みのアドレステーブルを使って
なフィルターを仕掛けているが、DHCPv6-PDでglobal-prefixをLAN側に告知した場合、globalな通信に関したWAN側と同じprefixが付くのでフィルターに引っかかってしまう
global-prefixをもらってくるか、さもなくばglobal IF同様にglobal-unicastを通過させるしかないか…
global-prefixを抜き取るアイデアとしては、rtsoldを改造して、M/O/Rオプションで外部scriptを起動するように、RA-prefixを受け取った時に外部scriptを起動させるという案がある
乗り換え作業中…
pfに関しては、pf.confの文法が本家OpenBSDにて途中で変わっていることもあって、ネット上の資料を読む際は要注意
あと、アドレスの代わりにインターフェース名(+装飾)でインターフェースアドレスやネットワーク・ブロードキャストアドレスを参照する機能があるが、natdのdynamicオプションやipfw_natのifオプションのように動的に適用されるのではなく、pfctlからルールをロードする際に展開してからコンパイル・最適化しているようなので、動的に生えるインターフェースに適用するにはanchor等でインターフェースが生えた時にルールをロードさせる or テーブル参照にしておいて、テーブルへのアドレス追加・削除で対応する等の策が必要な模様
これに関連して、proto icmpなルールで嵌る
例えば、
pass in on IF proto icmp from any to IF
だと、IFにv6アドレスがあると"to IF"がv6アドレスにも展開されるので、IPv6にはicmp(icmpはICMP/IPv4の事で、ICMPv6はicmp6)が無いのでエラーになる 逆に、v6インターフェースIPv6に
pass in on IFv6 proto icmp6 from any to IFv6
は機能するが、v4アドレスを追加すると問題が発生する
追記・挿入系の基本操作の整理
concatinateは、sequence後方に領域を確保するrealloc操作と、contentsへの上書きコピー操作で構成されているが、insertなどの処理も含めて一般化を考えると、以下の3種類の基本操作の組み合わせと解すべき
3種のうちreallocはallocation APIとして実装済みなので、残り2種類を実装する
APIの基本形は
書き込み先領域後端が、sequence containerからあふれる場合は、自動拡張される方が使い易いか?
また、大きなhole gapを開ける操作をサポートするにはdst-beginがsequnce contents後端より後ろを指すことを許容すべきだが、負のindexが指定された場合の解釈はどうするか?
現行仕様では、contents終端(n)と確保済みの長さ(nmax)を保持するのみで、realloc等で要求した長さは保持していない
in-kernel NAT試験でnatd + ipfw/divertとipfw in-kernel NATでincomming packetに対する挙動が異なる件だが、原因判明
sysctl net.inet.ip.fw.one_passが1(default)だと、natアクション作用後にパケットはipfwに戻ってこない
nat configでdeny_inオプションが指定されてない場合は、NATテーブルによる逆変換・redirectを受けなかったincommingパケットは破棄されず(deny_inなら破棄される)・ipfwにも戻らないので、inet層に届くことになり実質的に変換されないパケットにpassアクションが適用されたことになる Orz
divertアクションはsysctlによらずipfwに戻ってくる動作と比べると、分かりづらい挙動であるので要注意
beamline先頭・末尾の要素がOFFSET付きのMARKerで、要素内部を指しているケースで、multi-turn trackingを行う場合に周回trackingでの停止位置・開始位置をどうするか?
先頭・末尾のどちらかでfull element trackingを実施して、反対側のpartial elementは省略するのが筋が良さそうだが、意図的に変態モデリングされてるケースはどうすべきだろう?
ipfwからpfに切り替えて、NAT66を導入してLAN側のUnique Local AddressからNAT66経由でWAN側のIPv6 worldに到達を確認
やはり、コア時間帯は、PPPoEなIPv4 downlinkの帯域とジッターがぼろぼろなので、IPv4 over IPv6 (MAP-E)の導入を考えるべきか…
DynamicDNSで外部公開しているサービスを考えるとMulti FIB構成で、Jail側のサービスをPPPoEルート、一般のIPv4通信をIPv4 over IPv6側に送る構成にしたい
とりあえず、MAP-Eのパラメータ設定と境界ルーターの情報を調べないと…Orz
別FIB上で、試してみた
まずは、IPv6のWAN側のGlobal Unicast Addressから、MAP-Eに必要なパラメータを手に入れる (例えば、 http://ipv4.web.fc2.com/map-e.html )
IPv4アドレスが不足気味なので、CE IPv4 addressは固定値になる模様
最低限の設定は、以下の通り
setfibして、fetch等でIPv4系にリクエストすると、IPv4 over IPv6(MAP-E)経由で降りてきた
ただし、手元の実験だとbind-addressオプションでLAN側のIPから発信しないとうまく通信出来ない?
どうやら起こっているのは、以下の状況の模様
したがって、gifトンネルを保持するホストからIPv4 over IPv6 (MAP-E)で通信を行うには、下記の条件が必要
CE-IPv4-addressをgifトンネルに張り付ける場合の、pf natルール (<lan-ip4>は、LAN内のアドレス集合)
nat on gif# from gif#:0 to any -> gif#:0 map-e-portset offset/length/PSID nat on gif# from <lan-ip4> to any -> gif#:0 map-e-portset offset/length/PSID
実運用では、変換し損ねた内部アドレスが漏れないように、境界でのアドレスクラスフィルタリングを実施すること
運用上の考察
当面の作業目標
7/23 18:55頃測定
Connection | Down | Up | ping www.google.co.jp |
IPv4 PPPoE | 49.22Mbps | 80.42MBps | 9.804/14.806/25.310/5.180 ms (drop 1/20) |
IPv4 over IPv6 | 89.22Mbps | 78.46Mbps | 10.149/10.693/14.603/0.916 ms (drop 0/20) |
IPv6 | 89.64Mbps | 83.30Mbps | 10.225/10.621/12.651/0.508 ms (drop 0/20) |
設定スクリプトの自動化実装完了し、default FIBのdefault routeをIPv4 over IPv6(MAP-E)に切り替え実施
接続スクリプトの主たる機能
一応、経路試験用にFIB 1は、PPPoE側をdefault routeに指定
実は、RAで自動設定されるIPv6のdefault routeは、default FIBにのみ設定されているので、そのままではdefault FIB以外ではv6で外に出られない罠
後、v6 gatewayのnexthopアドレスが、Link Local Unicastになっているので、そのままsetfib 1 route -6 add default fe80:...%IFで登録しても、v6パケットの送り先がNexthopのLink Local Unicast Addressを向かず通信出来ない
どうやらdefault routeをNexthop Link Local Unicastへ向ける前に、route add nexthop-link-local-unicast -iface interfaceでdefault routeへ登録するNexthop Link Local Unicastがどのinterface上に居るかを登録すれば流れるっぽぃ
カテゴリー: Admin | Emacs | EPICS | Fortran | FreeBSD | GCC | hgsubversion | IPv6 | KEKB | LHC | Lisp | LLVM | MADX | Ryzen | SAD | samba | tDiary | unix | WWW | YaSAI | お仕事 | イベント | 出張 | 宴会 | 数学 | 艦これ | 買いもの | 追記 | 雑記