2018/9/3

Phoenix_1_Installation


Phoenix 是一個 Productive. Reliable. Fast. 的 web framework,以 elixir 實作,運作在 erlang VM 上。


installation


  • erlang vm

  • elixir

  • hex: elixir's package manager


    mix local.hex
  • MySQL/PostgreSQL

  • Node.js for Assets


    必須要 v5.3.0 以上


    node --version
  • Phoenix


    $ mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
  • mix phoenix.new


    mix 會增加 phoenix.new 的選項


    $ mix -h
    ...
    mix phoenix.new       # Creates a new Phoenix v1.2.5 application

create hello project,預設 DB 是使用 PostgreSQL,我們改用 MySQL


$ mix phoenix.new hello --database mysql

* creating hello/config/config.exs
* creating hello/config/dev.exs
* creating hello/config/prod.exs
* creating hello/config/prod.secret.exs
* creating hello/config/test.exs
* creating hello/lib/hello.ex
* creating hello/lib/hello/endpoint.ex
* creating hello/test/views/error_view_test.exs
* creating hello/test/support/conn_case.ex
* creating hello/test/support/channel_case.ex
* creating hello/test/test_helper.exs
* creating hello/web/channels/user_socket.ex
* creating hello/web/router.ex
* creating hello/web/views/error_view.ex
* creating hello/web/web.ex
* creating hello/mix.exs
* creating hello/README.md
* creating hello/web/gettext.ex
* creating hello/priv/gettext/errors.pot
* creating hello/priv/gettext/en/LC_MESSAGES/errors.po
* creating hello/web/views/error_helpers.ex
* creating hello/lib/hello/repo.ex
* creating hello/test/support/model_case.ex
* creating hello/priv/repo/seeds.exs
* creating hello/.gitignore
* creating hello/brunch-config.js
* creating hello/package.json
* creating hello/web/static/css/app.css
* creating hello/web/static/css/phoenix.css
* creating hello/web/static/js/app.js
* creating hello/web/static/js/socket.js
* creating hello/web/static/assets/robots.txt
* creating hello/web/static/assets/images/phoenix.png
* creating hello/web/static/assets/favicon.ico
* creating hello/test/controllers/page_controller_test.exs
* creating hello/test/views/layout_view_test.exs
* creating hello/test/views/page_view_test.exs
* creating hello/web/controllers/page_controller.ex
* creating hello/web/templates/layout/app.html.eex
* creating hello/web/templates/page/index.html.eex
* creating hello/web/views/layout_view.ex
* creating hello/web/views/page_view.ex

Fetch and install dependencies? [Yn] y
* running mix deps.get
* running npm install && node node_modules/brunch/bin/brunch build

We are all set! Run your Phoenix application:

    $ cd hello
    $ mix phoenix.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phoenix.server

Before moving on, configure your database in config/dev.exs and run:

    $ mix ecto.create

設定 DB 連線資訊


config/dev.exs


config :hello, Hello.Repo,
  adapter: Ecto.Adapters.MySQL,
  username: "root",
  password: "password",
  database: "hello",
  hostname: "localhost",
  pool_size: 10

執行 ecto.create


$ mix ecto.create

啟動 phoenix


$ mix phoenix.server
[info] Running Hello.Endpoint with Cowboy using http://localhost:4000
16:42:29 - info: compiled 6 files into 2 files, copied 3 in 1.1 sec

或是用 IEx (Interactive Elixir) 啟動


$ iex -S mix phoenix.server

網址在 http://localhost:4000



Simple tutorial


在 /web/router.ex 裡面有一段 scope 的定義


  scope "/", Hello do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
  end

所有以 ./ 開頭的 route 都會符合這個規則,pipe_through :browser macro 負責處理一般 browser 的 requests。


增加 /hello 的網址,指定給另一個 Controller


    get "/hello", HelloController, :world
    get "/", PageController, :index

http://localhost:4000/hello 會出現錯誤訊息


UndefinedFunctionError at GET /hello
function Hello.HelloController.init/1 is undefined (module Hello.HelloController is not available)

新增 /web/controllers/hello/hello_controller.ex


defmodule Hello.HelloController do
  @moduledoc false

  use Hello.Web, :controller
  def world(conn, _params) do
    render conn, "world.html"
  end

end

再瀏覽一次 http://localhost:4000/hello 會出現不同的錯誤訊息


UndefinedFunctionError at GET /hello
function Hello.HelloView.render/2 is undefined (module Hello.HelloView is not available)

新增 /web/views/hello_view.ex


defmodule Hello.HelloView do
  use Hello.Web, :view
end

新增 /web/templates/hello/world.html.eex


<h1>From template: Hello world!</h1>

不需要重新啟動 server,reload 網頁就可以看到結果




修改 web/router.ex,把網址當作變數


get "/hello/:name", HelloController, :world

修改 hello_controller.ex


defmodule Hello.HelloController do
  @moduledoc false

  use Hello.Web, :controller
  # external 參數定義為 "name" => name,但內部卻是用atom,也就是 name: name,這是因為 atom table 不會被 GC。
  def world(conn, %{"name" => name}) do
    render conn, "world.html", name: name
  end

end

修改 world.html.eex


<h1>Hello <%= String.capitalize @name %>!</h1>

瀏覽網址 http://localhost:4000/hello/test




lib 跟 web 資料夾的用途


supervision trees 及 long-running
processes 要放在 lib


web-related code 包含
models, views, templates, and controllers 這些放在 web


當 code reloading 功能打開時,web 資料夾裡面的程式在被修改後,會自動 reload,但是 lib 資料夾的程式並不會自動 reload,因此 lib 很適合放 long-running services,例如 Phoenix’s PubSub system, the database connection pool 或是自訂的 supervised processes。


.exs 是 Elixir scripts,不會編譯為 .beam files,因此 mix 設定檔 mix.exs 是用 script 而不是 .ex


Phoenix 支援master configuration 加上其他不同環境的設定檔,所以可看到主設定檔 config.exs 裡面有一段


import_config "#{Mix.env}.exs"

就是說明,這是透過 MIX_ENV 環境變數,判斷 prod/dev/test 三種環境,會嵌入不同環境的設定檔


dev.exs
prod.exs
test.exs

另外 production 環境有一個獨立另外嵌入的設定檔 prod.secret.exs,這是用來儲存 production 環境需要被保護的一些密碼,獨立的檔案可保持設定不會進入 git server。


Endpoint


config.exs 包含了 loggin, endpoint 的設定


Endpoint 是 web server 處理 connection 的介面,設定檔裡面只有一個 Hello.Endpoint


# Configures the endpoint
config :hello, Hello.Endpoint,
  url: [host: "localhost"],
  root: Path.dirname(__DIR__),
  secret_key_base: "g8c9YZ5dYGeA.....XkLz5",
  render_errors: [accepts: ~w(html json)],
  pubsub: [name: Hello.PubSub,
           adapter: Phoenix.PubSub.PG2]

而 /lib/hello/endpoint.ex 中 Hello.Endpoint 裡面包含了這些 chaing of functions 及 plugs


defmodule Hello.Endpoint do
    use Phoenix.Endpoint, otp_app: :hello
    
    plug Plug.Static, ...
    plug Plug.RequestId
    plug Plug.Logger
    plug Plug.Parsers, ...
    plug Plug.MethodOverride
    plug Plug.Head
    plug Plug.Session, ...
    plug Hello.Router
end

這在內部其實是用 pipeline 做的,Endpoints 就是對每一個 request 執行 chain of functions。


connection
    |> Plug.Static.call
    |> Plug.RequestId.call
    |> Plug.Logger.call
    |> Plug.Parsers.call
    |> Plug.MethodOverride.call
    |> Plug.Head.call
    |> Plug.Session.call
    |> Hello.Router.call

Endpoint 也是一個 plug,尤其他 plugs 組合而成。application 是以 series of plugs 組成,由 endpoint開始,以 controller 結束,最後一個 plug 是 controller,也就是 web/router.ex 定義的 controller。


connection
    |> endpoint
    |> plug
    |> plug
    ...
    |> router
    |> HelloController

通常 application 只需要一個 endpoint,但也沒有限制只能有一個,如果要支援 http 80 及 https 443 或是 特別的 8080 for admin,就可以增加 endpoints


Router Flow


web/router.ex 是由兩個部分組成的: pipelines 及 route table


defmodule Hello.Router do
  use Hello.Web, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", Hello do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
  end

  # Other scopes may use custom stacks.
  # scope "/api", Hello do
  #   pipe_through :api
  # end
end

有時會需要針對不同的網址,提供不同的 set of tasks/transformations,例如 :browser 是針對 HTML,有提供取得 session 的方法,並有一個稱為 flash 的 user message system,這裡也提供 security service,例如 request forgery protection。


第二個 pipeline 為 :api,是針對 JSON 的 API,只會處理 JSON requests,如果像要轉換為只處理 XML,就要修改這裡的 plug。


hello application 只使用了 :browser,是用
pipe_through :browser
指定的。


一般的 Phoenix application 是這樣組成的


connection
|> endpoint
|> router
|> pipeline
|> controller

  • endpoint: functions for every request
  • connection: 會經過 named pipeline,針對幾種主要類型的 request提供一般化的 functions
  • controller: 會呼叫 data model 並透過 view template 產生頁面

Controllers, Views, and Templates


web 目錄為


└── web
    ├── channels
    ├── controllers
    │ ├── page_controller.ex
    │ └── hello_controller.ex
    ├── models
    ├── static
    ├── templates
    │ ├── hello
    │ │ └── world.html.eex
    │ ├── layout
    │ │ └── app.html.eex
    │ ├── page
    │ │ └── index.html.eex
    ├── views
    │ ├── error_view.ex
    │ ├── layout_view.ex
    │ ├── page_view.ex
    │ └── hello_view.ex
    ├── router.ex
    └── web.ex

最底層為 router.ex 及 web.ex,web.ex 定義了整個 application structure。


在後面會提到 channel 的部分。


  • Erlang VM 會提供 application scalability
  • endpoint 能過濾 static request,並 parse request 然後呼叫 router
  • browser pipeline 會處理 "Accept" header,取得 session,也能防止 Cross-Site Request Forgery(CSRF) 的攻擊

hello application 相關的檔案


connection                      # Plug.Conn
    |> endpoint                 # lib/hello/endpoint.ex
    |> browser                  # web/router.ex
    |> HelloController.world    # web/controllers/hello_controller.ex
    |> HelloView.render(        # web/views/hello_view.ex
        "world.html")           # web/templates/hello/world.html.eex

References


Programming Phoenix


Using MySQL with the Phoenix Framework

2018/8/27

微型加密演算法 Tiny Encryption Algorithm (TEA)

Tiny Encryption Algorithm (TEA) 微型加密演算法是劍橋大學電腦實驗室的 David Wheeler 與 Roger Needham 在 1994年於 Fast Software Encryption workshop 發表的加密演算法。因為該演算法速度快,很容易實作,也很容易設計電路,因此常常被使用。


這個演算法在內地的很有名氣,主要因為騰訊 QQ 大量使用了這個演算法,進行資料加密的工作。


tea.c


原本找到的文件,提供了一個 C 語言的版本。因為 TEA 是針對 byte array 進行加解密,實作時要注意,是使用哪一種 CPU,如果是 Intel CPU,都是 Little Endian 的 byte order。


#include <stdio.h>
#include <stdint.h>

void encipher(unsigned long *const v,unsigned long *const w,
   const unsigned long *const k)
{
   register unsigned long y=v[0], z=v[1], sum=0;
   register unsigned long delta= 0x9E3779B9;
   register unsigned long a=k[0], b=k[1], c=k[2], d=k[3];
   register unsigned long n=32;

   while(n-->0)
      {
      sum += delta;
      y += (z << 4)+a ^ z+sum ^ (z >> 5)+b;
      z += (y << 4)+c ^ y+sum ^ (y >> 5)+d;
      }

   w[0]=y; w[1]=z;
}

void decipher(unsigned long *const v,unsigned long *const w,
   const unsigned long *const k)
{
   register unsigned long y=v[0], z=v[1];
   register unsigned long delta=0x9E3779B9;
   register unsigned long sum= delta * 32;
   register unsigned long a=k[0],b=k[1], c=k[2], d=k[3];
   register unsigned long n=32;

   /* sum = delta<<5, in general sum = delta * n */

   while(n-->0)
      {
      z -= (y << 4)+c ^ y+sum ^ (y >> 5)+d;
      y -= (z << 4)+a ^ z+sum ^ (z >> 5)+b;
      sum -= delta;
      }

   w[0]=y; w[1]=z;
}


#define htonl(A) ((((unsigned long)(A) & 0xff000000) >> 24) | \
    (((unsigned long)(A) & 0x00ff0000) >> 8) | \
    (((unsigned long)(A) & 0x0000ff00) << 8) | \
    (((unsigned long)(A) & 0x000000ff) << 24))

#define ntohl htonl
#define ENCRYPTED_VALUE_POSITION_IN_CONN_REQ 16

void printlong(unsigned long l) {
    unsigned long nl = ntohl(l);
    printf("%lX ", nl);
}

int main() {
    unsigned long u_auth_key[4] = {0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00};
    char random_num[8] = {0x72, 0x13, 0x8F, 0x90, 0x57, 0x68, 0x29, 0xaa };

    unsigned long encrypted_num[2] = {0};
    unsigned long * p_rand_num = (unsigned long *) random_num;
    char xnl_conn_req[24] = {0};
    // 由 big endiness 轉為 little
    *p_rand_num = ntohl(*p_rand_num);
    *( p_rand_num + 1) = ntohl(*(p_rand_num + 1));

    encipher(p_rand_num, encrypted_num, u_auth_key);
    // 由 little 轉為 big
    encrypted_num[0] = htonl(encrypted_num[0]);
    encrypted_num[1] = htonl(encrypted_num[1]);

    // printf("%lx %lx\n", encrypted_num[0], encrypted_num[1]);
    printlong(encrypted_num[0]);
    printlong(encrypted_num[1]);

}

但是編譯後發現,加密後的結果是錯誤的。原因在於 unsigned long 這個資料型別,因為該資料型別在 64位元 CPU 的機器上,資料長度超過 32 bits。解決方式是在編譯時,要加上 -m32 這個參數。


$ gcc -m32 tea.c
$ ./a.out
C5E65710 A9CA5E6F

tea2.c


在 TEA Wiki 也提供了一個 C 語言的加解密版本,可以注意到他使用了 uint32_t 這個資料型別,uint32_t 在 UNIX 系統上,是固定為 32-bit unsigned interger,不管 CPU 是 32bits 或是 64bits 都一樣,所以使用這個資料型別會比 long 來得好。


tea2.c


#include <stdio.h>
#include <stdint.h>

void encrypt(uint32_t* v, uint32_t key[4], uint32_t delta) {
  uint32_t v0=v[0], v1=v[1], sum=0, i;             // set up
  // uint32_t delta=0x12345678;                    // a key schedule constant
  for (i=0; i < 32; i++) {
    sum += delta;
    v0 += ((v1<<4) + key[0]) ^ (v1 + sum) ^ ((v1>>5) + key[1]);
    v1 += ((v0<<4) + key[2]) ^ (v0 + sum) ^ ((v0>>5) + key[3]);
  }
  v[0]=v0; v[1]=v1;
}

void decrypt (uint32_t* v, uint32_t key[4], uint32_t delta) {
    uint32_t v0=v[0], v1=v[1], i;  // set up
    // uint32_t sum=0x468ACF00;
    // uint32_t delta=0x12345678;  // a key schedule constant
    uint32_t sum = delta * 32;  // delta << 5

    // printf("%X %X %X\n", v0, v1, sum);
    for (i=0; i<32; i++) {
      v1 -= ((v0<<4) + key[2]) ^ (v0 + sum) ^ ((v0>>5) + key[3]);
      v0 -= ((v1<<4) + key[0]) ^ (v1 + sum) ^ ((v1>>5) + key[1]);
      sum -= delta;
      // printf("%X %X %X\n", v0, v1, sum);
    }
    v[0]=v0; v[1]=v1;
}

int main()
{
    uint32_t v[] = {0x72138F90, 0x576829AA};

    uint32_t key[4]={0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00};
    uint32_t delta=0x9E3779B9;

    printf("Original Values: ");
    printf("%X %X\n", v[0], v[1]);

    encrypt(v, key, delta);

    printf("Encrypted      : ");
    printf("%X %X\n", v[0], v[1]);

    decrypt(v, key, delta);
    printf("Decrypted      : ");
    printf("%X %X\n", v[0], v[1]);
    return 0;
}

執行結果如下


$ gcc tea2.c
$ ./a.out
Original Values: 72138F90 576829AA
Encrypted      : C5E65710 A9CA5E6F
Decrypted      : 72138F90 576829AA

tea.py


另外如果是 python,可以參考 C 語言的寫法,直接用 ctypes 進行開發。


tea.py


#!/usr/bin/env python
#-*- coding: utf-8 -*-

import sys
from ctypes import *

def encipher(v, k):
    y = c_uint32(v[0])
    z = c_uint32(v[1])
    sum = c_uint32(0)
    delta = c_uint32(0x9E3779B9).value
    n = 32
    w = [0,0]

    while(n>0):
        sum.value += delta
        y.value += ( z.value << 4 ) + k[0] ^ z.value + sum.value ^ ( z.value >> 5 ) + k[1]
        z.value += ( y.value << 4 ) + k[2] ^ y.value + sum.value ^ ( y.value >> 5 ) + k[3]
        n -= 1

    w[0] = y.value
    w[1] = z.value
    return w

def decipher(v, k):
    y = c_uint32(v[0])
    z = c_uint32(v[1])
    delta = c_uint32(0x9E3779B9).value
    sum = c_uint32(delta * 32)
    n = 32
    w = [0,0]

    while(n>0):
        z.value -= ( y.value << 4 ) + k[2] ^ y.value + sum.value ^ ( y.value >> 5 ) + k[3]
        y.value -= ( z.value << 4 ) + k[0] ^ z.value + sum.value ^ ( z.value >> 5 ) + k[1]
        sum.value -= delta
        n -= 1

    w[0] = y.value
    w[1] = z.value
    return w

if __name__ == "__main__":
    key = [0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00]
    v = [0x72138F90, 0x576829AA]

    enc = encipher(v,key)
    print "Original Values: " + ' '.join([format(i, 'x').upper() for i in v])

    print "Encrypted:       " + ' '.join([format(i, 'x').upper() for i in enc])

    dec = decipher(enc,key)
    print "Decrypted:       " + ' '.join([format(i, 'x').upper() for i in dec])

執行結果


$ python tea.py
Original Values: 72138F90 576829AA
Encrypted:       C5E65710 A9CA5E6F
Decrypted:       72138F90 576829AA

TEA.java


這是 Java 語言的版本


public class TEA {

    public static int[] encrypt(int[] block, int[] key) {
        int i = block[0];
        int j = block[1];
        int sum = 0;
        int delta = 0x9E3779B9;

        for (int k = 0; k < 32; ++k) {
            sum += delta;
            i += (j << 4 & 0xfffffff0) + key[0] ^ j + sum ^ (j >> 5 & 0x7ffffff) + key[1];
            j += (i << 4 & 0xfffffff0) + key[2] ^ i + sum ^ (i >> 5 & 0x7ffffff) + key[3];
        }

        block[0] = i;
        block[1] = j;

        return block;
    }

    public static int[] decrypt(int[] block, int[] key) {
        int i = block[0];
        int j = block[1];
        int delta = 0x9E3779B9;
        int sum = delta*32;   // sum=468ACF00
        // System.out.println("sum="+byteToUnsignedHex(sum));

        // System.out.println("i, j, sum="+byteToUnsignedHex(i)+", "+byteToUnsignedHex(j)+", "+byteToUnsignedHex(sum));
        for (int k = 0; k < 32; ++k) {
            j -= (i << 4 & 0xfffffff0) + key[2] ^ i + sum ^ (i >> 5 & 0x7ffffff) + key[3];
            i -= (j << 4 & 0xfffffff0) + key[0] ^ j + sum ^ (j >> 5 & 0x7ffffff) + key[1];
            sum -= delta;

            // System.out.println("i, j, sum="+byteToUnsignedHex(i)+", "+byteToUnsignedHex(j)+", "+byteToUnsignedHex(sum));
        }

        block[0] = i;
        block[1] = j;

        return block;
    }


    public static String byteToUnsignedHex(int i) {
        String hex = Integer.toHexString(i).toUpperCase();
        while(hex.length() < 8){
            hex = "0" + hex;
        }
        return hex;
    }

    public static String intArrToHex(int[] arr) {
        StringBuilder builder = new StringBuilder(arr.length * 8 + arr.length-1);
        for (int b : arr) {
            builder.append(byteToUnsignedHex(b));
            builder.append(" ");
        }
        return builder.toString();
    }

    public static void main(String[] args){
        int[] key = new int[]{0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00};
        int[] data = new int[]{0x72138F90, 0x576829AA};
        System.out.println("Original Values: "+intArrToHex(data));

        int[] enc = TEA.encrypt(data, key);
        System.out.println("Encrypted      : "+intArrToHex(enc));

        int[] dec = TEA.decrypt(enc, key);
        System.out.println("Decrypted      : "+intArrToHex(dec));
    }
}

執行結果


$ javac TEA.java
$ java TEA
Original Values: 72138F90 576829AA
Encrypted      : C5E65710 A9CA5E6F
Decrypted      : 72138F90 576829AA

References


Tiny Encryption Algorithm


Java/J2ME implementation of the Tiny Encryption Algorithm


TEA、XTEA、XXTEA加密解密算法


介紹XXTEA加密算法


XXTEA encryption arithmetic library


TEA和QQTEA


TEA加密算法java版


一個長整數各自表述 (in 64-bit system)


Tiny-Encryption-Algorithm


TEA算法在QQ中的應用

2018/8/20

Lua Support in Wireshark


wireshark 是網路封包解析工具,但如果是新的或是自己開發的 protocol,就必須要自行開發封包解析的 plugin,讓 wireshark 也能看得懂新的協定的封包。


wireshark 是以 C 開發的,所以可以用 C 語言開發 plugin,但還有另一個比較快速的方式,就是使用 lua script 開發,Lua 在 wireshark 可支援撰寫 dissectors, post-dissectors and taps。


dissector 就是封包解析的 plugin,雖然可以用 lua 快速開發 dissector,但是執行速度還是比 C 語言開發的 plugin 慢。不過如果要快速完成 dissector 開發,lua 還是一個好的選擇,畢竟不需要花時間 compile, debug C dissector。


post-dissectors 是在每一個 dissector 執行後,可以運作的 plugin,可用來增加 dissection tree,建立自訂的 filtering mechanism。


taps 是針對被 dissected 的封包,用來收集資訊的 plugin。


installation


由 wireshark 官網下載安裝套件安裝後,在 About Wireshark 選單中,可查閱 wireshark 的資訊。如果在中間有看到 "with Lua 5.2.4" 的字串,就表示這個 wireshark 有支援 lua。



另外,要查閱 Folders 頁籤,要注意 Personal Lua Plugins, Global Lua Plugins, Global Configuration 的路徑



Global Configuration: /Applications/Wireshark.app/Contents/Resources/share/wireshark

Personal Lua Plugins: /Users/charley/.local/lib/wireshark/plugins

Global Lua Plugins: /Applications/Wireshark.app/Contents/PlugIns/wireshark

安裝 lua plugin


有三種方式


  1. 放在 Personal Lua Plugins 目錄中
  2. 修改 init.lua,以 dofile() 載入 lua script
  3. 在啟動 wireshark 的 command line 中,加上 "-X lua_script:pcap_file.lua" 參數

如果已經做好了 plugin,可以直接放在 Personal Lua Plugins 目錄中,複製進去後,就可以在 About Wireshark -> Plugins 頁籤,看到那些 lua script。


第二種方式,先找到 init.lua 的存放位置,在這台機器上是放在 Global Configuration 目錄中。


編輯 init.lua,可看到最後面是 dofile(DATA_DIR.."console.lua")


# 備份 init.lua
cp /Applications/Wireshark.app/Contents/Resources/share/wireshark/init.lua /Applications/Wireshark.app/Contents/Resources/share/wireshark/init.lua.bak

vi /Applications/Wireshark.app/Contents/Resources/share/wireshark/init.lua

.....
dofile(DATA_DIR.."console.lua")
--dofile(DATA_DIR.."dtd_gen.lua")

要注意 init.lua 的最前面 disable_lua 必須要是 false


disable_lua = false

if disable_lua then
    return
end

在 init.lua 最後面加上


LUA_SCRIPT_PATH="/Users/charley/.wireshark/lua/"
dofile(LUA_SCRIPT_PATH.."hello.lua")

編輯 hello.lua
vi /Users/charley/.wireshark/lua/hello.lua


-- hello.lua
print("hello world!")

以 command line wireshark 測試


$ tshark -x lua_script:hello.lua
hello world!
Capturing on 'FireWire'

References


使用 wireshark 的 lua script


Lua編寫Wireshark插件實戰


Lua


Chapter 10. Lua Support in Wireshark

2018/8/13

如何編譯 RPi kernel


以下紀錄如何編譯 Raspberry Pi Kernel


查詢 RPi 上安裝的 kernel 版本號碼


$ uname -a
Linux raspberrypi 4.14.34-v7+ #1110 SMP Mon Apr 16 15:18:51 BST 2018 armv7l GNU/Linux

找一台 x86 linux 電腦,以 cross-compile 方式重新編譯 RPi kernel,雖然可以直接在 RPi 編譯,但速度會很慢,因此還是選擇用 cross-compile 方式編譯。


RPi kernel 存放在 git repository 裡面


cd
mkdir raspberry
cd raspberry

確認有安裝 git, 並更新一些套件(需要安裝nss 相關和更新curl)


yum update git
yum update nss nss-util nspr
yum update curl



下載 kernel source


git clone https://github.com/raspberrypi/linux.git

下載 cross-compilers tools


git clone https://github.com/raspberrypi/tools

使用 tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian,設定環境變數 CCPREFIX


export CCPREFIX=/root/raspberry/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-

${CCPREFIX}gcc -v

在 GitHub 上面的 kernel 有許多不同的 branch,請選擇你想要使用的 branch 來使用。


cd linux
git branch -a

目前版本為


* rpi-4.14.y
  remotes/origin/HEAD -> origin/rpi-4.14.y



由 RPi 取得目前的 kernel 設定,因為 RPi 上沒有 config.gz,用以下指令產生 /proc/config.gz


sudo modprobe configs

將 /proc/config.gz 複製到要編譯 kernel 的機器上的 /root/raspberry/linux 目錄中


cd /root/raspberry/linux
scp pi@192.168.1.15:/proc/config.gz . 
gunzip -c config.gz > .config

用以下指令產生新的 kernel configuration,如果遇到新的功能,就會問你要不要支援


ARCH=arm CROSS_COMPILE=${CCPREFIX} make oldconfig

reset .config 裡面的 debug symbols,Enable the ‘DEBUGINFO’ option and don’t enable the DEBUGINFO_REDUCED option.


grep -v DEBUG_INFO < .config > newconfig 
mv newconfig .config 
ARCH=arm CROSS_COMPILE=${CCPREFIX} make oldconfig



以 menuconfig 微調設定,調整 kernel modules support。


make -j4 ARCH=arm CROSS_COMPILE=${CCPREFIX} menuconfig



編譯 kernel,如果是 multi-core cpu,可加上 -j<amount of cores> 加速編譯


ARCH=arm CROSS_COMPILE=${CCPREFIX} make

如果編譯過程中,有發生 compile error,打開錯誤的 source code,在發生錯誤的 function 前面加上


#pragma GCC optimize("-g0")

編譯完成後,必須檢查 symbols,用以下指令,查看 initutsns.name.release 的值


${CCPREFIX}gdb vmlinux 
print init_uts_ns.name.release

確認 kernel 有包含 symbols 後,將 RPi 需要的 modules 放在 modules 目錄


ARCH=arm CROSS_COMPILE=${CCPREFIX} INSTALL_MOD_PATH=../modules make modules_install



建立 uncompressed kernel image,並放到 RPi 的 tmp 目錄中


cd <raspberry pi downloads>/tools/mkimage 
./imagetool-uncompressed.py ../../linux/arch/arm/boot/zImage
scp kernel.img pi@raspberrypi:/tmp

將 modules 目錄,壓縮後,傳送到 RPi 的 tmp 目錄中


cd <raspberry pi downloads>/tools/modules 
tar czf modules.tgz * 
scp modules.tgz pi@raspberrypi:/tmp

最後 ssh 到 RPi,安裝新的 kernel 及 modules


cd / 
sudo mv /tmp/kernel.img /boot/ 
sudo tar xzf /tmp/modules.tgz 
rm /tmp/modules.tgz

reboot RPi


sudo shutdown -r now



以 uname 檢查是否使用了新版的 kernel


uname -r



如果要 debug kernel ,可參考 Preparing Raspberry PI for JTAG Debugging 的說明


References


https://sysprogs.com/VisualKernel/tutorials/raspberry/buildkernel/


https://blog.gtwang.org/iot/raspberry-pi-compile-linux-kernel/


https://www.raspberrypi.com.tw/528/compiling-kernel-for-raspberry-pi/


https://coldnew.github.io/f5873b3f/

2018/8/6

Architect 軟體架構師該做什麼事情?


架構 architecture 這個名詞是源於建築學,就是指建築物在設計上,是如何利用內部的支撐物相互結合,而製作出一個穩固的建築物的方法。而架構師 architect 則是為滿足某種架構設計目標,而進行整體建物的構思與設計的角色。在軟件系統工程領域,借用了建築學的 architecture 以及 architect 這兩個名詞,將軟體系統架構設計者,也稱為 architect。


Architect 這個角色跟 Project Manager 最根本的不同點,是 Project Manager 偏向管理面,比較重視專案的進度、成本、資源等管理項目,而 Architect 雖然也會進行管理工作,但只針對技術面,實作面,負責掌握所有開發者的做事方式及進度,有點像是 Technology Project Manager 的角色。


梓人傳


唐代的柳宗元,撰寫了一篇文章「梓人傳」,非常貼切地描寫了一位房屋建築師的工作寫照,文章的第一段,就明確地讓梓人自述他平常的工作內容跟收入:「吾善度材。視棟宇之制,高深圓方短長之宜,吾指使而群工役焉。舍我,眾莫能就一宇。故食於官府,吾受祿三倍;作於私家,吾收其宜大半焉。」語譯為「我擅長計算材料,看房子的規格,知道高深圓方短長所需要的木材。我指揮工人們去工作。沒有我,工人們就沒有人能蓋好一幢房子。如果我在政府機關裏做事,我領別人三倍的薪水。在私人住宅裡工作,我收的工錢也比別人多一大半。」


作者還是覺得這個人很奇怪的,為什麼不是拿著刀鋸斧斤,只會用尺規墨斗,領到的錢還比其他人多。甚至舉了例子,梓人家裡的椅子壞了,還要找工人來修理。後來作者實際到了梓人工作的地方,觀察了很久,發現他指揮大家做事,掌握進度,調度工人。最後完工後,還寫上他的名字以示負責。


軟體工程感覺上跟建築業很像,也需要一個 architect 出面,進行指揮調度的工作。不過有一點最大的差異,建築業的 architect 只需要進行設計,還能掌握成品的品質,就算他從來沒拿過鋸子,不拿工具,也可以完成工作。而軟體開發比較不一樣,沒有真正實作過一個軟體系統的經驗,是沒辦法直接用眼睛看,就看出軟體開發的奧秘,所以基本上,不大可能會出現一個從來沒有寫過程式的 architect。


架構師該做什麼?


接下來由不同的文章,來看看軟體架構師應該要做什麼,要會什麼?


抽象、分層、分治和演化思維

優秀架構師必須掌握的架構思維


架構的本質是管理複雜性,抽象、分層、分治和演化思維是架構師應對和管理複雜性的四種最基本武器。


抽象:架構師先要在大腦中形成抽象概念,然後是子模塊分解,然後是依次實現子模塊,最後將子模塊拼裝組合起來,形成最後系統。


分層:把整個系統劃分成若干個層次,每一層專註解決某個領域的問題,並向上提供服務。有些層次是縱向的,它貫穿所有其它層次,稱為共享層。分層也可以認為是抽象的一種方式,將系統抽象分解成若干層次化的模塊。


分治:分而治之(divide and combine或者split and merge) 把大問題分解成若干個子問題,如果子問題還無法直接解決,則繼續分解成子子問題,直到可以直接解決的程度,這個是分解(divide)的過程;然後將子子問題的解組合拼裝成子問題的解,再將子問題的解組合拼裝成原問題的解,這個是組合(combine)的過程。


架構既是設計出來的,同時也是演化出來的,對於互聯網系統,基本上可以說是三分設計,七分演化,而且是在設計中演化,在演化中設計,一個不斷迭代的過程。從單塊架構開始,隨著架構師對業務域理解的不斷深入,也隨著業務和團隊規模的不斷擴大,漸進式地把單塊架構拆分成微服務架構的思路,這就是演化式架構的思維。


廣度、高度、深度、寬度

如何成為一個架構師?


廣度指的是架構師應該對所在領域的主流技術體系有一個全面清晰的認識,每一種技術不需要很深入的瞭解,但必須知道每種技術的3W:1,Why:每種技術的由來,為什麼會出現這種技術,這個技術是用來解決什麼問題的?2,What:每種技術是什麼?技術的基本組成部分是什麼?3,Which:解決同一問題的相同技術各自的優缺點是什麼,更適合哪種場景?比如,ORM框架(Hibernate與IBatis),MVC框架(Struts與SpringMVC),大數據技術(Hadoop與Spark)它們各自的優缺點是什麼,只有清晰認識同一類型技術的優缺點,才能在技術選型時能夠使用更加合理的技術。


高度指的是架構師應具備對客觀事物的拔高能力,能夠從紛繁雜亂的信息中建立秩序,也就是我們一般所說的抽象能力。


深度指的是架構師能對主流技術有較為深入的理解,可以不瞭解源代碼,但對主流技術的原理,運作機理有一個基本的理解。


寬度指的是架構師能夠熟知當前的技術前沿和熱點,能夠使用新的技術解決問題。比如,微服務、大數據、雲計算、人工智能等。


廣度決定了系統架構技術選型的合理性
高度決定了系統架構設計的合理性
深度決定了系統架構的優化能力
寬度決定了系統架構的領先性,不至於三五年被淘汰


確認需求、系統分解、技術選型、制定技術規格

如何成為一個架構師?


確認需求:架構師要懂得用戶需求,理解用戶真正想要什麼,這使得架構師必須要和分析人員不斷溝通,反覆確認需求規格說明書,以此來保證他精準清楚用戶需求。


系統分解:在架構師認可需求規格說明書後,架構師已明確用戶需求是是什麼,這時候便看架構師的分解能力了。一般分為縱向分解和橫向分解,縱向分解是將整個系統分層,從而將整體系統分解成下一級的子系統與組件。橫向分解是在系統分解成不同的邏輯層或服務後,對邏輯層進行分塊,確定層與層之間的關係。


技術選型:在系統分解後,架構師會最終形成軟件整體架構,接下來,架構師的職責是技術選型。「前端到底用瘦客戶端還是富客戶端呢?數據庫是用MySQL還是MSSQL又或是Oracle呢?」在瞭解用戶需求後,分解完系統後,技術選型是非常重要的環節,提出各個方向,再進行評估。架構師在技術選型階段會提供參考信息給專案經理,專案經理再從預算、進度、人力、資源等各方面情況來權衡,最終確認。


制定技術規格:架構師在項目開發過程中是「靈魂人物」,並且要具備協調組織能力和懂得人員分工。在制定技術規格說明階段,架構師要協調起所有的開發人員,架構師通常會用技術規格說明書與開發人員保持溝通,讓開發人員能從各個視角去觀測、理解他們負責的模塊或者子系統,確保開發人員能夠按照架構意圖實現各項功能。


設計能力、技術實力、溝通能力

如何成為一個架構師?


設計能力:擅長整合分析架構是過程,並非結果。架構是架構師洞察內在結構、原則、規律與邏輯的過程,架構師要做到清晰理解系統,以及簡潔描述,這是分析整合的能力。一個架構師必須具備極強的分析能力,要做到根據產品宗旨和目標,分析清楚產品定位以及產品業務,再整合利用現有的技術領域,找出最佳方案,實現產品概念。


技術實力:實現產品規劃架構師首先要將代碼寫的清晰易懂,要能夠實現功能,做到沒有Bug,這要求架構師必須具備至少熟練掌握一門語言。這是最重要的,每一名出色的架構師,必定是一位優秀程序員。架構師並不是純粹的管理崗位,對那些愛寫各式文檔、畫流程圖、脫離代碼、只說不做、高高在上的架構師,程序員們通常會稱他們為——PPT 架構師。不懂編程的架構師的職業生涯必定是短暫的,無論如何都不可本末倒置,要想實現自己的職業規劃,不能荒廢自己本身的技能,技術是架構師賴以生存的最基本能力。


溝通能力:能夠橫向溝通架構師必須參與項目開發全過程,包括確認需求、系統分解、架構設計、技術選型、制定技術規格說明、系統實現、集成測試和部署各階段,在這一系列過程中,架構師會與各部門溝通交流。一個產品會有多部門合作,架構師在其中的溝通極為重要,直接影響產品進度與質量。架構師不僅要與開發人員溝通,也要和項目經理、分析人員甚至用戶溝通,來實現產品的各種可能性。所以,對於架構師來講,不僅有技術方面的要求,還有能夠橫向溝通的要求。


什麼是架構?架構師的職責是什麼?


設計能力:擅長整合分析
技術實力:實現產品規劃
溝通能力:能夠橫向溝通


References


四個架構設計案例分析及其背後的架構師思維


{程序員邀稿} 從軟體架構師(Architect)的觀點來看軟體開發流程


淺談技術型專案經理和管理型專案經理