hogeとはワイルドカードのようなものです。日々起こった、さまざまなこと −すなわちワイルドカード− を取り上げて日記を書く、という意味で名付けたのかというとそうでもありません。適当に決めたらこんな理由が浮かんできました。
02/08/2021 ふむ [長年日記]
■ [Py][小ネタ] llesync 再び
数年前に趣味で書いたもの だが,先日新調した PC の上で何となく動かしてみたら思ったより速かったので,結果を README に載せておいた. 何だろうね.以前の結果は Disk I/O で律速してたっぽいから,SSD になったお陰で恩恵を受けられるようにでもなったのかな. あるいは単に kernel が高速化されたか. 調べてないので分からんけど.
まあいずれにせよ単純にコピーする場合において cp には勝てない.っょぃ.
10/04/2019 ふむ [長年日記]
■ [Linux][小ネタ] netplan と systemd-networkd と macvlan とわたし
現状の netplan (0.97) では直接 macvlan インタフェースを作れないらしい.しょうがないので直接 networkd の設定を書いて作ることを考える.
下記のようなインタフェースがあったとして,
# cat /etc/netplan/02-netdev.yaml network: version: 2 renderer: networkd ethernets: ens3: dhcp4: no ens4: dhcp4: no ens5: dhcp4: no
ens5 から macvlan インタフェース vmac0 を生やしてみる. networkd の設定はこんな感じになる.
# cat /etc/systemd/network/00-ens5.network [Match] Name=ens5 [Network] MACVLAN=vmac0 LinkLocalAddressing=no # cat /etc/systemd/network/90-vmac0.netdev [NetDev] Name=vmac0 Kind=macvlan MACAddress=00:00:00:00:00:01 [MACVLAN] Mode=private # cat /etc/systemd/network/90-vmac0.network [Match] Name=vmac0 [Network] LinkLocalAddressing=no
上記はうまくいく例で,下記のように,意図通りにインタフェースが生成される.
# ip -d link show dev vmac0 6: vmac0@ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000 link/ether 00:00:00:00:00:01 brd ff:ff:ff:ff:ff:ff promiscuity 0 macvlan mode private addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
さてここからがハマりどころの話.
netplan はその設定情報を元に /run/systemd/network/10-netplan-<インタフェース名>.network というファイルを生成するようだ.上記の例では ens5 が netplan の設定に書かれているため,下記のようなファイルが生成される.
# cat /run/systemd/network/10-netplan-ens5.network [Match] Name=ens5 [Network] LinkLocalAddressing=ipv6
勘の良い諸兄はもうお分かりだろう.上記「うまくいく例」がうまくいくのは,ens5 の networkd 設定を 00-ens5.network という名前にして,netplan が書き出すものより優先的に使用されるようにしたからである. これを例えば 90-ens5.network などと netplan が書き出すものより弱いファイル名にすると,意図通りにインタフェースが生成されず,涙で枕を濡らすことになってしまうことだろう.
もちろん macvlan の元インタフェース (上記例では ens5) については netplan の設定に書かない,という選択もある. しかしいずれにしても設定が分散することになるので,全国津々浦々の macvlan ファンにとって現状の netplan が嬉しくないことに変わりはないだろう.netplan 先生の次回作に期待である.
01/23/2019 うむ [長年日記]
■ [Linux][小ネタ] Linux で Private VLAN (pvlan) もどき
本物の pvlan を実装するのはちょっと面倒な気がしたので,簡単にできそうな擬似的な "もどき" を作ってみる.ひとまず Cisco で言うところの switchport mode private-vlan trunk promiscuous な uplink を想定.
実現には,ブリッジにおいて uplink 想定のポートとの通信以外を遮断して,uplink との通信時にはネットワーク毎に primary vlan 相当のタグを付加してやれば良いはずなので,vlan_filtering と ebtables を用いて実装してみる.テストのためのお膳立てが多いので長くなってしまっているが,メインは "# create nsx" というコメントから.
ip netns add nsx # pseudo pvlan box ip netns add ns1 # uplink (trunk) ip netns add ns2 # downlink (10) ip netns add ns3 # downlink (10) ip netns add ns4 # downlink (20) ip netns add ns5 # downlink (20) # create ns1 ip link add veth0 type veth peer name veth1 ip link set veth0 netns ns1 ip link set veth1 netns nsx ip -n ns1 link add veth0.10 link veth0 type vlan id 10 ip -n ns1 link add veth0.20 link veth0 type vlan id 20 ip -n ns1 addr add 10.0.10.1/24 dev veth0.10 ip -n ns1 addr add 10.0.20.1/24 dev veth0.20 for dev in lo veth0 veth0.10 veth0.20; do ip -n ns1 link set "${dev}" up done # create ns2 ip link add veth0 type veth peer name veth2 ip link set veth0 netns ns2 ip link set veth2 netns nsx ip -n ns2 addr add 10.0.10.2/24 dev veth0 for dev in lo veth0; do ip -n ns2 link set "${dev}" up done # create ns3 ip link add veth0 type veth peer name veth3 ip link set veth0 netns ns3 ip link set veth3 netns nsx ip -n ns3 addr add 10.0.10.3/24 dev veth0 for dev in lo veth0; do ip -n ns3 link set "${dev}" up done # create ns4 ip link add veth0 type veth peer name veth4 ip link set veth0 netns ns4 ip link set veth4 netns nsx ip -n ns4 addr add 10.0.20.4/24 dev veth0 for dev in lo veth0; do ip -n ns4 link set "${dev}" up done # create ns5 ip link add veth0 type veth peer name veth5 ip link set veth0 netns ns5 ip link set veth5 netns nsx ip -n ns5 addr add 10.0.20.5/24 dev veth0 for dev in lo veth0; do ip -n ns5 link set "${dev}" up done # create nsx ip -n nsx link add brx type bridge vlan_filtering 1 ip -n nsx link add br10 type bridge ip -n nsx link add br20 type bridge ip -n nsx link add p10a type veth peer name p10b ip -n nsx link add p20a type veth peer name p20b for dev in lo brx br10 br20 p10a p10b p20a p20b veth1 veth2 veth3 veth4 veth5; do ip -n nsx link set "${dev}" up done ip -n nsx link set veth1 master brx ip -n nsx link set p10a master brx ip -n nsx link set p20a master brx ip -n nsx link set p10b master br10 ip -n nsx link set veth2 master br10 ip -n nsx link set veth3 master br10 ip -n nsx link set p20b master br20 ip -n nsx link set veth4 master br20 ip -n nsx link set veth5 master br20 # allow vid 10,20 as tagged on veth1 (uplink) bridge -n nsx vlan add dev veth1 vid 10 bridge -n nsx vlan add dev veth1 vid 20 bridge -n nsx vlan del dev veth1 vid 1 # allow vid 10 as untagged on p10a (veth2,3) bridge -n nsx vlan add dev p10a vid 10 pvid untagged bridge -n nsx vlan del dev p10a vid 1 # allow vid 20 as untagged on p20a (veth4,5) bridge -n nsx vlan add dev p20a vid 20 pvid untagged bridge -n nsx vlan del dev p20a vid 1 # deny downlink-to-downlink communication on br10 ip netns exec nsx ebtables -A FORWARD --logical-in br10 -i p10b -j ACCEPT ip netns exec nsx ebtables -A FORWARD --logical-out br10 -o p10b -j ACCEPT ip netns exec nsx ebtables -A FORWARD --logical-in br10 -j DROP ip netns exec nsx ebtables -A FORWARD --logical-out br10 -j DROP # deny downlink-to-downlink communication on br20 ip netns exec nsx ebtables -A FORWARD --logical-in br20 -i p20b -j ACCEPT ip netns exec nsx ebtables -A FORWARD --logical-out br20 -o p20b -j ACCEPT ip netns exec nsx ebtables -A FORWARD --logical-in br20 -j DROP ip netns exec nsx ebtables -A FORWARD --logical-out br20 -j DROP
そんなに大したことをやっているわけではなく,ブリッジを 2段に分けて,上位ブリッジでは下位ブリッジとやりとりするフレームにタグ付けを行い,下位ブリッジではポート間通信制御を行っているだけ.
当然 "もどき" であって本物の pvlan ではないので,例えば trunk port を用意して延伸するとかはできない.何せこの実装には secondary vlan が存在しないので当たり前である.
かしこ
追記
ちなみにわざわざブリッジを分けずとも,downlink port 毎に pvid を指定していけばブリッジ 1つで済ませることもできる.のだが,そうすると downlink port が増えるごとに,ブリッジへの参加だけでなく pvid の設定もやる必要が出てきて面倒なので,ここでは分離する方式を採っている.例えば libvirt/kvm で VM を生やして,I/F を上記 br10, 20 に参加させるといったことを考えると分かりやすい.
12/16/2018 ふむ [長年日記]
■ [Py][小ネタ] NAPT 前のアドレスを知る
例えば下記のようなネットワークがあるとする.Client が Web Server にアクセスした際,NAPT Router によって NAPT される前の Client の情報を Web Server が知りたいとする.
+--------+ +-------------+ +------------+ | Client |----------| NAPT Router |--------| Web Server | +--------+ +-------------+ +------------+ 172.16.0.193 172.16.0.1 10.207.9.88 10.207.9.87
NAPT Router が Linux 箱なら,conntrack エントリがカーネルの中に保持されているので,下記のようなサービスを NAPT Router の上で動かしておくことができるだろう.
import flask from pyroute2.netlink.exceptions import NetlinkError from pyroute2.netlink.nfnetlink.nfctsocket import NFCTSocket, NFCTAttrTuple app = flask.Flask(__name__) cts = NFCTSocket() @app.route('/conntrack', methods=['GET']) def conntrack_get(): saddr = flask.request.args.get('saddr') daddr = flask.request.args.get('daddr') proto = int(flask.request.args.get('proto')) sport = int(flask.request.args.get('sport')) dport = int(flask.request.args.get('dport')) tpl = NFCTAttrTuple(saddr=saddr, daddr=daddr, proto=proto, sport=sport, dport=dport) try: entry = cts.entry('get', tuple_reply=tpl) except NetlinkError: return flask.Response(status=404) tuple_orig = entry[0].get_attr('CTA_TUPLE_ORIG') tuple_ip = tuple_orig.get_attr('CTA_TUPLE_IP') tuple_proto = tuple_orig.get_attr('CTA_TUPLE_PROTO') return flask.jsonify(saddr=tuple_ip.get_attr('CTA_IP_V4_SRC'), daddr=tuple_ip.get_attr('CTA_IP_V4_DST'), proto=tuple_proto.get_attr('CTA_PROTO_NUM'), sport=tuple_proto.get_attr('CTA_PROTO_SRC_PORT'), dport=tuple_proto.get_attr('CTA_PROTO_DST_PORT')) if __name__ == '__main__': app.run('10.207.9.88')
その上で Web Server が下記のように,このサービスを叩く.
import flask import requests app = flask.Flask(__name__) @app.route('/', methods=['GET']) def root(): saddr = flask.request.environ['SERVER_NAME'] daddr = flask.request.environ['REMOTE_ADDR'] sport = flask.request.environ['SERVER_PORT'] dport = flask.request.environ['REMOTE_PORT'] response = requests.get('http://10.207.9.88:5000/conntrack?' 'saddr=%(saddr)s&daddr=%(daddr)s&proto=6&' 'sport=%(sport)s&dport=%(dport)s' % locals()) if not response.ok: return flask.Response(status=404) entry = response.json() return flask.jsonify(saddr=entry['saddr'], daddr=entry['daddr'], proto=entry['proto'], sport=entry['sport'], dport=entry['dport']) if __name__ == '__main__': app.run('10.207.9.87')
Client から Web Server にアクセスしてみると,
$ curl -s http://10.207.9.87:5000/ | jq . { "daddr": "10.207.9.87", "dport": 5000, "proto": 6, "saddr": "172.16.0.193", "sport": 55208 }
Web Server から,NAPT 前のオリジナルの Client のデータが返ってきていることが確認できる.
だからどうしたぼくドラえもん.
11/18/2017 ふむ [長年日記]
■ [Py][小ネタ] SQLAlchemy でグラフ
相変わらず長いこと放置しているので,秋の夜長にと言うには少し遅いけど,何となく遊び.
ノードに種類を持たせつつ,多対多でノード同士を繋いでみたりするなど.
import uuid import json from contextlib import contextmanager from sqlalchemy import create_engine from sqlalchemy import Table, Column, ForeignKey, String from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() Engine = create_engine('sqlite:///:memory:') Session = sessionmaker(bind=Engine) NodeAssoc = Table( 'node_assoc', Base.metadata, Column('parent_id', String(36), ForeignKey('node.id', onupdate='CASCADE', ondelete='CASCADE'), nullable=False), Column('child_id', String(36), ForeignKey('node.id', onupdate='CASCADE', ondelete='CASCADE'), nullable=False), ) @contextmanager def txn(): session = Session() try: yield session session.commit() except Exception: session.rollback() raise finally: session.close() class ModelBase: def __init__(self, **kwargs): self.id = str(uuid.uuid4()) self.apply(**kwargs) def apply(self, **kwargs): for key, val in kwargs.items(): setattr(self, key, val) def dictify(self, ignore=['id']): return {n: getattr(self, n) for n in self.__table__.columns.keys() if n not in ignore} def __repr__(self): return json.dumps(self.dictify()) class Node(ModelBase, Base): TYPE = None id = Column(String(36), primary_key=True) type = Column(String(255), nullable=False) value = Column(String(255)) children = relationship( 'Node', secondary=NodeAssoc, primaryjoin=id == NodeAssoc.columns.parent_id, secondaryjoin=id == NodeAssoc.columns.child_id, backref='parents', uselist=True) __tablename__ = 'node' __mapper_args__ = {'polymorphic_on': type} def __init__(self, **kwargs): super().__init__(**kwargs) self.type = self.TYPE class CircleNode(Node): TYPE = 'circle' __mapper_args__ = {'polymorphic_identity': TYPE} class SquareNode(Node): TYPE = 'square' __mapper_args__ = {'polymorphic_identity': TYPE} class TriangleNode(Node): TYPE = 'triangle' __mapper_args__ = {'polymorphic_identity': TYPE} if __name__ == '__main__': def dump(mode, nodes): def _dump(mode, node, level=1): print(' ' * level, node) for n in getattr(node, mode): _dump(mode, n, level + 2) print(mode) for node in nodes: _dump(mode, node) print() Base.metadata.create_all(bind=Engine) with txn() as session: t1 = TriangleNode(value='1') t2 = TriangleNode(value='2') t3 = TriangleNode(value='3') s1 = SquareNode(value='1', children=[t1, t2]) s2 = SquareNode(value='2', children=[t2, t3]) c1 = CircleNode(value='1', children=[s1, s2]) session.add(c1) with txn() as session: for mode, cls in [('children', CircleNode), ('parents', TriangleNode)]: dump(mode, session.query(cls).all())
実行するとこうなる.
children {"type": "circle", "value": "1"} {"type": "square", "value": "1"} {"type": "triangle", "value": "1"} {"type": "triangle", "value": "2"} {"type": "square", "value": "2"} {"type": "triangle", "value": "2"} {"type": "triangle", "value": "3"} parents {"type": "triangle", "value": "1"} {"type": "square", "value": "1"} {"type": "circle", "value": "1"} {"type": "triangle", "value": "2"} {"type": "square", "value": "2"} {"type": "circle", "value": "1"} {"type": "square", "value": "1"} {"type": "circle", "value": "1"} {"type": "triangle", "value": "3"} {"type": "square", "value": "2"} {"type": "circle", "value": "1"}
だからどうしたぼくドラえもん.
□ mped [色々、お久しぶりです。 昔、O2やオクティン、SGI辺りのUNIX…で、フィンガーデスグリップっていう、WINDO..]
□ mpet [mpedって、madな感じだな。久々過ぎて間違えたmpetです。 まぁ、unixとか、知らん世代だと、どっちにしろ..]