2024年5月28日火曜日
MochiOS開発記@当面の目標
2024年3月3日日曜日
CUnit@単体テストはじめ
はじめに
自作kernel作るにあたってDebug機能が欲しくなり。
snprintfが必要となり。
やっぱlibc欲しいなとlibcを作り始め(newlib使ってもよかったんだけど、自作楽しいので)。
snprintfを作ったのだが、これ動くのか?と。
単純に作業量2倍になるので、趣味でテスト系は避けてきたのだけど、やっぱ避けて通れないよねテスト。
というわけで、CUnit使うか・・・の備忘録。
ライブラリインストール
sudo apt-get install libcunit1-dev
テスト対象コード例(target.c)
int target_func( void )
{
return 5;
}
テストコード例
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
#include "target.c"
void test_func_1( void )
{
ret = target_func();
CU_ASSERT_EQUAL( ret, 5 );
}
int main( void )
{
CU_pSuite suite;
/* テストレジストリ初期化 */
CU_initialize_registry();
/* テストスイート追加 */
suite = CU_add_suite( "TestSuite", NULL, NULL );
/* テスト追加 */
CU_add_test( suite, "test1", test_func_1 );
/* テスト実行 */
CU_basic_set_mode( CU_BRM_VERBOSE );
CU_basic_run_tests();
/* テストレジストリ削除 */
CU_cleanum_registry();
return 0;
}
static関数をテストしたい場合も考慮して、テスト対象コードをinclude。
サブ関数をstub化
サブ関数をstub化する方法はいくつかあるが、テスト対象コードをいっさい変えずにやるのは無理っぽい。
下記が最善策かなぁ。検索してもいい案が出てこないので自分で考えてみた。
#ifndef CALL
#define CALL( a ) a
#endif
#ifndef PROTO
#define PROTO( a, b ) a b
#endif
PROTO( extern int, target_sub( void ) );
int target_sub( void )
{
return 5;
}
int target_func( void )
{
return CALL( target_sub() );
}
テストコードは、テスト対象コードのincludeの前に下記を定義しておく
#define CALL( a ) stub_##a
#define PROTO( a, b ) a stub_##b
テスト対象コードincludeの後に下記を定義する
int stub_target_sub( void )
{
return 5;
}
2022年11月22日火曜日
linux@VPN構築
はじめに
在宅勤務になってから、ほぼ外出することが無くなり、外出先から自宅ネットワークに繋ぐことも無くなっていた。久しぶりに繋いでみたら、繋がらない。どうやら使っていたDDNSサービス(ieserver)が終了したらしく、他にも色々と理由があってダメだったので、見直すことにした。
ネットワーク構成は下記の通り。
VPN serverは、Debian GNU/Linux 9 (stretch)
VPNClientは、WindowsだったりAndroidだったり。
ドメイン名設定
私の自宅インターネット接続環境は固定IPアドレスではなく、再接続なんかの度にコロコロIPアドレスが変わるので、外出先からはvpn.mochi.cоm(仮)みたいなドメイン名を使ったアクセスをできるようにする必要がある。
これは、どこかのDDNSサービスを使ってドメイン名を取得し、自宅サーバから定期的に自分のグローバルIPアドレスを更新してあげることで実現できる。
DDNSサービスの選択
今回、適当に検索して色々出てくるなか、下記サービスを使うことにした。
簡単に説明すると、
- 無料
- ホスト名:アカウント名.f5.si ※siはスロベニアらしい
- Aレコード設定可
- AAAAレコード設定可
- TXTレコード設定可
こんな素晴らしいサービスを無料で提供して頂けることに感謝。
そして、もともと使っていたieserver並みにシンプルに使える。
crondでIPを自動更新する
自宅のIPアドレスはインターネット再接続の度に変わってしまい、その都度手動でDDNSサービスに再登録するのは面倒な上に、外出先からはIPアドレスが判らず再登録できないので、VPNサーバを使って自動的にIPをDDNSサービスに再登録する様にする。
- crontabを開く
crontab -e
- 10分毎に実行するコマンドを追加する
0,10,20,30,40,50 * * * * wget -O DDNSNow_update.log "https://f5.si/update.php?domain=ユーザ名&password=パスワード"
パスワードはAPIトークンでもOK。というかAPIトークンがいいかな。
この設定だと、ログ(DDNSNow_update.log)はホームディレクトリに吐かれる。
PPPoE接続設定
以前の私のインターネットはPPPoE接続だったのだが、FTTHなのに通信速度が遅く、在宅勤務となった際に安定したビデオ会議が出来なかったため、IPoE・IPv4 over IPv6接続に切り替えていた。
ただし、この接続はIKEv2のVPNで使用する500番や4500番などの特定ポートが使用できないため、ここでも外出先からVPN接続できない要因となっていた。
これまでの環境は維持しながらVPN接続できるようにもしたいため、ルータでIPoE接続しつつPPPoEパススルーさせ、VPNサーバから直接PPPoE接続させることで、通常のインターネット通信は高速のまま外部からのVPN接続を可能にさせる。
(これは通信事業者・プロバイダによって出来たり出来なかったりするかも)
基本的なファイアウォール設定
VPNサーバもグローバルIPアドレスを獲得したわけなので、直接攻撃されることができて超危険。ファイアウォールを設定する。
- ufw(ファイアウォール)インストール
sudo apt-get install ufw
- 全アクセスの拒否
sudo ufw default deny
- ローカルLAN(192.168.1.*)からの全アクセスを全許可
sudo ufw allow in from 192.168.1.0/24
- ファイアウォール有効化
sudo ufw enable
- ログレベルの設定
お好みで。フラッシュメモリを温存したいので、ログを無効にする。
sudo ufw logging off
出したい場合はoffをonに、またはlowに。
pppoeconf設定
- pppoeconfインストール
sudo apt-get install pppoeconf
- 接続設定
pppoeconf
色々質問されるが、大体は「はい」を選択すれば問題ない。
PPPoE接続
pon dsl-provider
接続状況確認
ip address show
(参考)PPPoE切断
poff dsl-provider
VPNサーバ構築
認証方式の選択
VPNのサーバ/ユーザ認証方式は、相互に証明書を使った認証(EAP-TLS)にする。
下記のような鍵構成にする。
ユーザ証明書とサーバ証明書は、オレオレ認証局の個人鍵によって署名し、オレオレ認証局の公開鍵(ルート証明書)によって正しさを検証することができる。
VPN接続時、VPNサーバはクライアントにサーバ証明書を送信し、クライアントは事前配布済のルート証明書を使ってサーバを認証する。同様にクライアントはVPNサーバにクライアント証明書を送信し、ルート証明書を使ってクライアントを認証する。
(なお、VPNサーバにクライアント証明書を事前登録しておく理由は、失効済証明書で認証を防ぐため)
VPNサーバ設定
StrongSwanを使ってlinux上にVPNサーバを構築する。
StrongSwanインストール
sudo apt-get install strongswan
ipsecを設定する
/etc/ipsec.confを修正し、VPN構成を設定する。
conn ikev2-vpn
# 基本設定
auto=add
keyingtries=1
keyexchange=ikev2
# VPNサーバ設定
left=%any
leftsubnet=0.0.0.0/0
leftcert=server-cert.pem
leftid=vpn.mochi.com
leftsendcert=always
# VPNクライアント設定
right=%any
rightauth=eap-tls
rightcert=mochi-cert.pem
rightca="CN=vpn.mochi.com"
rightid=mochi
rightsourceip=192.168.1.192/26
rightdns=8.8.8.8,8.8.4.4
# VPNクライアント生存確認設定
dpddelay=60s
dpdtimeout=20s
dpdaction=clear
| 項目 | 説明 |
|---|---|
| auto | connのロード方法と接続タイミング。「add」は、ロードだけで接続しない(サーバ設定なので接続も何もない)設定 |
| keyingtries | 接続試行回数 |
| keyexchange | 鍵交換方法 |
| left | VPNサーバのローカルIPアドレス |
| leftcert | VPNサーバ証明書のパス(絶対パスまたは/etc/ipsec.d/certsからの相対パス) |
| leftid | VPNサーバ証明書のsubject(公開鍵の発行者)の識別名 |
| leftsendcert | VPNサーバ証明書をVPNクライアントに渡す方法 |
| right | VPNクライアント側のエンドポイントIPアドレス |
| rightauth | VPNクライアントの認証方法 |
| rightcert | VPNクライアント証明書のパス(絶対パスまたは/etc/ipsec.d/certsからの相対パス) |
| rightca | VPNクライアント証明書の検証に使用する認証局のDN(Distingished Name)。(/etc/ipsec.d/cacertsの中から該当するDNのルート証明書を使って検証される) |
| rightsourceip | VPNクライアントに割り当てるIPアドレスの範囲 |
| rightdns | VPNクライアントに伝えるDNSアドレス |
| dpddelay | 通信の生存確認間隔(デフォルトは30s) |
| dpdtimeout | 生存確認応答待ちタイムアウト(デフォルトは150s) |
| dpdaction | 生存確認できなかった場合のアクション。clearは接続終了を意味する。 |
ユーザ認証情報を設定する
サーバの秘密鍵もここに設定しておく。
/etc/ipsec.secretsを修正する
: RSA "server-key.pem"
鍵・証明書作成
作業ディレクトリ作成
下記の通り作業ディレクトリを作成して、そのディレクトリ内で作成する。
mkdir ~/pki
mkdir ~/pki/cacerts
mkdir ~/pki/certs
mkdir ~/pki/private
chmod go-rwx ~/pki/private
| ディレクトリ | 概要 |
|---|---|
| cacerts | ルート証明書を格納 |
| certs | 証明書を格納 |
| private | 個人鍵(秘密鍵)を格納 |
オレオレ認証局の個人鍵・証明書作成
- オレオレ認証局の個人鍵を作成する
pki --gen --type rsa --size 4096 --outform pem > ~/pki/private/ca-key.pem
- オレオレ認証局の公開鍵証明書(ルート証明書)を作成する
pki --self --ca --lifetime 3650 --in ~/pki/private/ca-key.pem --type rsa --dn "CN=ca.mochi.com" --outform pem > ~/pki/cacerts/ca-cert.pem
※CNの値は適切に変更すること。
VPNサーバの個人鍵・証明書作成
- サーバの個人鍵を作成する
pki --gen --type rsa --size 4096 --outform pem > ~/pki/private/server-key.pem
- サーバの公開鍵証明書(サーバ証明書)を作る
pki --pub --in ~/pki/private/server-key.pem --type rsa | pki --issue --lifetime 1825 --cacert ~/pki/cacerts/ca-cert.pem --cakey ~/pki/private/ca-key.pem --dn "CN=vpn.mochi.com" --san vpn.mochi.com --flag serverAuth --flag ikeIntermediate --outform pem > ~/pki/certs/server-cert.pem
※CNと-sanの値はDDNSサービスで取得したドメイン名に変更すること。
クライアントの個人鍵・証明書作成
- クライアントの個人鍵を作成する
pki --gen --type rsa --size 4096 --outform pem > ~/pki/private/mochi-key.pem
- クライアントの公開鍵証明書(クライアント証明書)を作る
pki --pub --in ~/pki/private/mochi-key.pem --type rsa | pki --issue --lifetime 1825 --cacert ~/pki/cacerts/ca-cert.pem --cakey ~/pki/private/ca-key.pem --dn "CN=mochi" --san mochi --outform pem > ~/pki/certs/mochi-cert.pem
※ファイル名は適切に変更すること。
※CNと-sanの値は適切に変更すること。
- pkcs12(pfx)形式に証明書を変換
openssl pkcs12 -export -inkey ~/pki/private/mochi-key.pem -in ~/pki/certs/mochi-cert.pem -certfile ~/pki/cacerts/ca-cert.pem -caname "ca.mochi.com" -name "mochi" -out ~/mochi.p12
これは、クライアント側で証明書をインストールする際に使用する。
作業ディレクトリコピー
/etc/ipsec.dに作業ディレクトリ内容をそのままコピーする
sudo cp -r ~/pki/* /etc/ipsec.d/
IPsec再起動
sudo ipsec restart
VPN用ファイアウォール設定
IKEv2の鍵交換でUDP500番と4500番を使用するため、下記を用いてそのポートを解放する。
sudo ufw allow 500,4500/udp
2021年11月23日火曜日
github@パスワード認証でpushできなくなった
はじめに
最近、漸くコードをイジれて、久しぶりにgithubにpushしようとしたのだが、
$ git push origin develop
Username for 'https://github.com': MasterMochi
Password for 'https://MasterMochi@github.com':
remote: Support for password authentication was removed on August 13, 2021.
Please use a personal access token instead.
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information.
fatal: Authentication failed for 'https://github.com/MasterMochi/mvfs.git/'
あれ、pushできな~い。なになに・・・
パスワード認証のサポートは2021年8月13日に削除されました~。
代わりにパーソナルアクセストークンを使ってくれ~
そういえば、以前pushした時にそんな警告が出ていた気がする。承認方法変わったのか。
8月ってダイブ前だな・・・
誘導されたページを見てみる
https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/
前置きはすっ飛ばして、"What you need to today"をテキトーに読むと、
今日すべきこと
- 開発者さんのために、もしGitHub.comでGit操作を承認するためにパスワード使ってんなら、HTTPS (推奨)かSSH鍵を介した パーソナルアクセストークン を使わなきゃダメよ。
うん、で?と思ったら、「パーソナルアクセストークン」にリンクがついてて、そこに飛ぶと生成方法が書いてあった。
パーソナルアクセストークン取得
- githubページにアクセス
- 自分のアイコンから「Settings」に飛ぶ
- 左のメニューから「Developer settings」に飛ぶ
- 左のメニューから「Personal access tokens」に飛ぶ
- 「Generate new token」をクリック
- 「Note」に適当にトークンの説明を書く
- 「Expiration」を適当に設定(No expirationにしちゃった…無期限)
- 「Select scopes」で「repo」にチェックいれる
- 「Generate token」をクリックしてトークンを取得
トークン使ってpush
passwordに取得したトークン入れるだけでOKだった。
2019年9月17日火曜日
MochiOS@仮想ファイルシステムの作成part10
前提知識無しで作るなんちゃって仮想ファイルシステムpart10。
開発日記です。独り言です。
これまでのあらすじ
| part | 内容 |
|---|---|
| 0 | 仮想ファイルシステムを作りたい宣言をする。 |
| 1 | ざっくり仮想ファイルシステムの機能を決める。 が、機能呼出し先の仮想ファイルシステムのタスクIDをどう取得するかという問題に気付く。 |
| 2 | カーネルにタスク名登録機能を実装した。 これでタスク名からタスクIDを取得する事が出来る様になるハズ、なので試したいが試す方法が無い事に気付き、ログ出力タスクの開発を宣言する。 |
| 3 | ログ出力の為に使うvsnprintf()の仕様を調べる。 |
| 4 | ログ出力タスクを開発してタスク名からタスクIDの取得機能が正しく動いている事が確認できた。 |
| 5 | mount機能を実装した。たぶんできてる。 |
| 6 | FDどうやって管理しようかー。メッセージのやり取り制御を状態遷移で制御しようかー。とか考えて、open機能を実装した。たぶんできてる。 |
| 7 | openと同じようなシーケンスなので、メッセージとちょっとやる事変えて簡単にwrite機能を実装できちゃった。 |
| 8 | writeと同じようなシーケンスなので、メッセージとちょっとやる事変えて簡単にread機能を実装できちゃった。 |
| 9 | readと同じようなシーケンスなので、メッセージとちょっとやる事変えて簡単にclose機能を実装できちゃった。 |
応用
これまでmount、open~closeまでさくっと何も考えずに実装してきた。(そういえばunmount実装してないな。まいいか)
全てはドライバとアプリ間のインタフェースを作ることがひとまずの目的だったので、ドライバを作ってみなければ。
というわけでns16550ドライバを作った。
ns16550は所謂、RS-232C、COMポート、シリアルポート、UARTなどと呼ばれているハードウェアで、割りと制御が簡単。
それぞれ16byteの転送バッファと受信バッファがあって、書き込む(VfsWrite)時は転送バッファがフルでなければ1byteづつ書き込んでフルの時は転送バッファ空割り込みが来るまで待つ。読み込む時は受信割り込みが来たときに1byteづつ読み込んでいたデータを返すだけ。
MochiKernelのメッセージパッシングはキューがなく、即座にメッセージ受信をしないと仮想ファイルサーバがブロックされてシステムの性能が悪くなってしまうので、メインと受信と転送用に1プロセス3スレッド構成に。
(MochiKernelにスレッド機能なんて無かったので、作った。)
とまぁ、これだけでかなりのネタになるんだけれど、割愛。
⇂がコミットログ。
https://github.com/MasterMochi/drv-ns16550/commit/a09994d07188ec84b663b902f6dbc07509cc44de
本題はこれから。
ttyサーバを作りたいが…
tty「サーバ」って書くと???になるのだけれど、要はttyドライバです。MochiOSではサーバープロセスとしてttyファイルを提供するので「サーバ」にしているだけです。
そんで考えたのだけれど、次のシーケンスで悩みが出てくる。
- ユーザアプリがttyサーバに行読込み(cockedモード)
- ttyサーバがドライバからデータ読込み
- 受信データ無しのため、読込サイズ0で応答。
(シーケンス図では、仮想ファイルサーバは省略。それぞれのインタフェースは仮想ファイルサーバ上で行われる。そのためVfsRead…Readのような形式で記載。上位からはReadを起動し、それを受け取って仮想ファイルサーバから下位へVfsReadが起動される。)
ttyサーバは行データが揃うまでユーザアプリへRead要求を返さない。行データが揃うまで、ドライバから文字を読み続けなければいけないわけだけど、ドライバは受信データが無い(EOF)ので直ぐに読込サイズ0でReadを返す。
(ところが上のコミットではドライバがブロックする設計ミス。。。修正する。)
今、実装済みの機能でこれを実現するには、ttyサーバはドライバへのReadを繰り返すしかない。
これは、もう、CPU100%一直線。
Select/VfsReady機能を追加する
説明しなくても大体わかると思うけど、selectです。指定したファイルディスクリプタの読み込みまたは書き込みが出来るまで待ち合わせます。
VfsReadyはファイルの読み込みまたは書き込み準備が出来た事を意味する通知。
いちいちSelectで指定した全てのFDに対応するMountプロセスにレディ状態を問い合わせると、メッセージのやり取りが増えて待ち合わせ状態の管理や時間がかかるので、レディ状態は仮想ファイルシステムでも保持する仕様にした。その為、VfsRead応答に最新のレディ状態を含める様に修正する。
以下、メッセージ仕様。
■ Select要求メッセージ
| offset | size | 内容 | 値 | 意味 |
|---|---|---|---|---|
| 0x00 | 4 | 機能ID | 0x0000_0009 | Select |
| 0x04 | 4 | タイプ(要求/応答) | 0x0000_0000 | 要求 |
| 0x08 | 4 | 読込監視グローバルFD数 | 0~1024 | - |
| 0x0C | 4 | 書込監視グローバルFD数 | 0~1024 | - |
| 0x10 | 4 | 読込監視グローバルFD[0] | 任意 | - |
| … | … | 読込監視グローバルFD[…] | … | … |
| 可変 | 4 | 書込監視グローバルFD[0] | 任意 | - |
| … | … | 書込監視グローバルFD[…] | … | … |
■ Select応答メッセージ
| offset | size | 内容 | 値 | 意味 |
|---|---|---|---|---|
| 0x00 | 4 | 機能ID | 0x0000_0009 | Select |
| 0x04 | 4 | タイプ(要求/応答) | 0x0000_0001 | 応答 |
| 0x08 | 4 | 処理結果 | 0x0000_0000 0x0000_0001 |
成功 失敗 |
| 0x0C | 4 | 読込レディグローバルFD数 | 0~1024 | - |
| 0x10 | 4 | 書込レディグローバルFD数 | 0~1024 | - |
| 0x14 | 4 | 読込レディグローバルFD[0] | 任意 | - |
| … | … | 読込レディグローバルFD[…] | … | … |
| 可変 | 4 | 書込レディグローバルFD[0] | 任意 | - |
| … | … | 書込レディグローバルFD[…] | … | … |
■ VfsReady通知メッセージ
| offset | size | 内容 | 値 | 意味 |
|---|---|---|---|---|
| 0x00 | 4 | 機能ID | 0x0000_0008 | vfsClose |
| 0x04 | 4 | タイプ(要求/応答) | 0x0000_0002 | 通知 |
| 0x08 | 4 | グローバルFD | 任意 | - |
| 0x0C | 4 | Ready対象 | 0bit目: read 1bit目: write |
読込みレディ 書込みレディ |
■ vfsRead応答メッセージ(改造)
| offset | size | 内容 | 値 | 意味 |
|---|---|---|---|---|
| 0x00 | 4 | 機能ID | 0x0000_0006 | vfsRead |
| 0x04 | 4 | タイプ(要求/応答) | 0x0000_0001 | 応答 |
| 0x08 | 4 | グローバルFD | 任意 | - |
| 0x0C | 4 | 処理結果 | 0x0000_0000 0x0000_0001 |
成功 失敗 |
| 0x10 | 4 | 読込レディ状態 | 0x0000_0000 0x0000_0001 |
非レディ レディ |
| 0x14 | 4 | 読込み実施サイズ | 0~24,064 | - |
| 0x18 | 0~24,064 | データ | 任意 | - |
■ vfsWrite応答メッセージ(改造)
| offset | size | 内容 | 値 | 意味 |
|---|---|---|---|---|
| 0x00 | 4 | 機能ID | 0x0000_0004 | vfsWrite |
| 0x04 | 4 | タイプ(要求/応答) | 0x0000_0001 | 応答 |
| 0x08 | 4 | グローバルFD | 任意 | - |
| 0x0C | 4 | 処理結果 | 0x0000_0000 0x0000_0001 |
成功 失敗 |
| 0x10 | 4 | 書込レディ状態 | 0x0000_0000 0x0000_0002 |
非レディ レディ |
| 0x10 | 4 | 書込み実施サイズ | 0~24,064 | - |
実装してみた
ttyプロセスが、select->read->writeを無限に繰り返す様にお試し。COM1で「1234567890Select!」を入力してみた。上のウィンドウは画面でデバッグログを、下のウィンドウはCOM1。端っこ切れてる。。
深く説明はしないけど、まぁCPU100%にならずに読込み待ちできたって感じです。
ここで問題発生
これまで、突貫で適当に作りすぎてて、機能毎に複数の要求を扱えない。
なので、ttyファイルをReadするとその先のドライバファイルのReadが行えない状況に。。。
直さねば。全体的に大改造なので、次回の更新はいつになるかな。
Rust@naked関数
Rust@naked関数 multiboot仕様でブートローダを自作せずGRUBから自作カーネル(もどき)を起動できたので、起動情報も色々受け取りたい。multiboot準拠していれば、起動直前にブートローダがレジスタEAXにマジック値を...
-
git@よく使うコマンド早見表 gitでよく使うコマンドをまとめる。 add コマンド 説明 git add . ワークディレクトリ配下全てのファイルをステージ(インデックスに記録)する git ad...
-
MochiOS@仮想ファイルシステムの作成part7 前提知識無しで作るなんちゃって仮想ファイルシステムpart7。 開発日記です。独り言です。 これまでのあらすじ part 内容 0 仮想ファイルシステムを作...
-
突然のフリーズ。 カーネルログを見ても解析困難。 というわけで、このバグ修正を当面の目標にしよう。 INT14なのでページフォルトなのだけど、誰かがメモリ破壊してる気がするなぁ。。。 とりあえず、カーネルログのレベル実装してもうちょい情報増やしてみよう。
