トップ 最新 追記

本 日 の h o g e

hogeとはワイルドカードのようなものです。日々起こった、さまざまなこと −すなわちワイルドカード− を取り上げて日記を書く、という意味で名付けたのかというとそうでもありません。適当に決めたらこんな理由が浮かんできました。

更新情報の取得には rdflirs を使ってもらえると嬉しいです.


12/16/2018 ふむ [長年日記]

tDiary 5897日目

[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 のデータが返ってきていることが確認できる.

だからどうしたぼくドラえもん.