From 48bfe83c0f6fd2df29cdf6c8e3ff825e834fdeae Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Sun, 28 Apr 2019 00:01:51 +0900 Subject: [PATCH] Add a Document --- docs/.gitignore | 3 + docs/Gemfile | 24 ++ docs/Gemfile.lock | 77 ++++ docs/_config.yml | 107 +++++ docs/_docs/guide/01-quickstart.md | 233 ++++++++++ .../guide/02-building-nextepc-from-sources.md | 400 ++++++++++++++++++ docs/_docs/platform/01-debian-ubuntu.md | 108 +++++ docs/_docs/platform/02-centos.md | 145 +++++++ docs/_docs/platform/03-fedora.md | 131 ++++++ docs/_docs/platform/04-freebsd.md | 134 ++++++ docs/_docs/platform/05-macosx.md | 141 ++++++ docs/_docs/platform/06-ubuntu-trusty.md | 157 +++++++ docs/_docs/tutorial/01-your-first-lte.md | 320 ++++++++++++++ docs/_includes/disqus_comments.html | 20 + docs/_includes/footer.html | 42 ++ docs/_includes/google-analytics.html | 10 + docs/_includes/head-includes.html | 0 docs/_includes/head.html | 23 + docs/_includes/header.html | 24 ++ docs/_includes/icon-github.svg | 4 + docs/_layouts/allposts.html | 39 ++ docs/_layouts/compress.html | 10 + docs/_layouts/default.html | 23 + docs/_layouts/home.html | 52 +++ docs/_layouts/page.html | 14 + docs/_layouts/post.html | 33 ++ docs/_pages/404.html | 25 ++ docs/_pages/about.md | 29 ++ docs/_pages/docs.md | 21 + docs/_pages/faq.md | 293 +++++++++++++ docs/_pages/github.md | 5 + docs/_pages/home.md | 5 + docs/_posts/2017-02-01-programming-started.md | 9 + docs/_posts/2017-10-09-release-v0.1.0.md | 11 + docs/_posts/2017-10-11-release-v0.1.1.md | 12 + docs/_posts/2017-11-02-release-v0.2.0.md | 14 + docs/_posts/2017-12-18-release-v0.3.0.md | 14 + docs/_posts/2017-12-31-release-v0.3.1.md | 12 + docs/_posts/2018-02-13-release-v0.3.3.md | 20 + docs/_posts/2018-03-06-release-v0.3.5.md | 20 + docs/_posts/2018-03-21-release-v0.3.6.md | 15 + docs/_posts/2018-04-14-release-v0.3.7.md | 12 + docs/_posts/2018-06-03-release-v0.3.9.md | 23 + docs/_posts/2018-08-17-release-v0.3.10.md | 21 + docs/_posts/2019-04-27-release-v0.3.11.md | 28 ++ docs/_sass/minima-reboot.scss | 6 + docs/_sass/minima-reboot/_layout.scss | 73 ++++ .../minima-reboot/_syntax-highlighting.scss | 61 +++ docs/_sass/minimal-mistakes.scss | 11 + docs/_sass/minimal-mistakes/_notices.scss | 100 +++++ docs/_sass/minimal-mistakes/_variables.scss | 159 +++++++ docs/assets/css/main.scss | 5 + docs/assets/favicon.ico | Bin 0 -> 1150 bytes docs/assets/pcapng/srsenb.pcapng | Bin 0 -> 8600 bytes docs/assets/pcapng/testepc.pcapng | Bin 0 -> 142636 bytes docs/assets/webui/install | 151 +++++++ docs/assets/webui/uninstall | 27 ++ 57 files changed, 3456 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/Gemfile create mode 100644 docs/Gemfile.lock create mode 100644 docs/_config.yml create mode 100644 docs/_docs/guide/01-quickstart.md create mode 100644 docs/_docs/guide/02-building-nextepc-from-sources.md create mode 100644 docs/_docs/platform/01-debian-ubuntu.md create mode 100644 docs/_docs/platform/02-centos.md create mode 100644 docs/_docs/platform/03-fedora.md create mode 100644 docs/_docs/platform/04-freebsd.md create mode 100644 docs/_docs/platform/05-macosx.md create mode 100644 docs/_docs/platform/06-ubuntu-trusty.md create mode 100644 docs/_docs/tutorial/01-your-first-lte.md create mode 100644 docs/_includes/disqus_comments.html create mode 100644 docs/_includes/footer.html create mode 100644 docs/_includes/google-analytics.html create mode 100644 docs/_includes/head-includes.html create mode 100644 docs/_includes/head.html create mode 100644 docs/_includes/header.html create mode 100644 docs/_includes/icon-github.svg create mode 100644 docs/_layouts/allposts.html create mode 100644 docs/_layouts/compress.html create mode 100644 docs/_layouts/default.html create mode 100644 docs/_layouts/home.html create mode 100644 docs/_layouts/page.html create mode 100644 docs/_layouts/post.html create mode 100644 docs/_pages/404.html create mode 100644 docs/_pages/about.md create mode 100644 docs/_pages/docs.md create mode 100644 docs/_pages/faq.md create mode 100644 docs/_pages/github.md create mode 100644 docs/_pages/home.md create mode 100644 docs/_posts/2017-02-01-programming-started.md create mode 100644 docs/_posts/2017-10-09-release-v0.1.0.md create mode 100644 docs/_posts/2017-10-11-release-v0.1.1.md create mode 100644 docs/_posts/2017-11-02-release-v0.2.0.md create mode 100644 docs/_posts/2017-12-18-release-v0.3.0.md create mode 100644 docs/_posts/2017-12-31-release-v0.3.1.md create mode 100644 docs/_posts/2018-02-13-release-v0.3.3.md create mode 100644 docs/_posts/2018-03-06-release-v0.3.5.md create mode 100644 docs/_posts/2018-03-21-release-v0.3.6.md create mode 100644 docs/_posts/2018-04-14-release-v0.3.7.md create mode 100644 docs/_posts/2018-06-03-release-v0.3.9.md create mode 100644 docs/_posts/2018-08-17-release-v0.3.10.md create mode 100644 docs/_posts/2019-04-27-release-v0.3.11.md create mode 100644 docs/_sass/minima-reboot.scss create mode 100644 docs/_sass/minima-reboot/_layout.scss create mode 100644 docs/_sass/minima-reboot/_syntax-highlighting.scss create mode 100644 docs/_sass/minimal-mistakes.scss create mode 100644 docs/_sass/minimal-mistakes/_notices.scss create mode 100644 docs/_sass/minimal-mistakes/_variables.scss create mode 100644 docs/assets/css/main.scss create mode 100644 docs/assets/favicon.ico create mode 100644 docs/assets/pcapng/srsenb.pcapng create mode 100644 docs/assets/pcapng/testepc.pcapng create mode 100644 docs/assets/webui/install create mode 100644 docs/assets/webui/uninstall diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..45c150536 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,3 @@ +_site +.sass-cache +.jekyll-metadata diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 000000000..ad16d5073 --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,24 @@ +source "https://rubygems.org" + +# Hello! This is where you manage which Jekyll version is used to run. +# When you want to use a different version, change it below, save the +# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: +# +# bundle exec jekyll serve +# +# This will help ensure the proper Jekyll version is running. +# Happy Jekylling! + +gem "jekyll", "~> 3.8.5" +gem "minima", "~> 2.0" +gem 'jekyll-seo-tag' + +# If you want to use GitHub Pages, remove the "gem "jekyll"" above and +# uncomment the line below. To upgrade, run `bundle update github-pages`. +# gem "github-pages", group: :jekyll_plugins + +# If you have any plugins, put them here! +group :jekyll_plugins do + gem "jekyll-feed", "~> 0.6" + gem "jekyll-redirect-from" +end diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock new file mode 100644 index 000000000..e9907f600 --- /dev/null +++ b/docs/Gemfile.lock @@ -0,0 +1,77 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.6.0) + public_suffix (>= 2.0.2, < 4.0) + colorator (1.1.0) + concurrent-ruby (1.1.5) + em-websocket (0.5.1) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0.6.0) + eventmachine (1.2.7) + ffi (1.10.0) + forwardable-extended (2.6.0) + http_parser.rb (0.6.0) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + jekyll (3.8.5) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 0.7) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 2.0) + kramdown (~> 1.14) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (>= 1.7, < 4) + safe_yaml (~> 1.0) + jekyll-feed (0.12.1) + jekyll (>= 3.7, < 5.0) + jekyll-redirect-from (0.15.0) + jekyll (>= 3.3, < 5.0) + jekyll-sass-converter (1.5.2) + sass (~> 3.4) + jekyll-seo-tag (2.6.0) + jekyll (~> 3.3) + jekyll-watch (2.2.1) + listen (~> 3.0) + kramdown (1.17.0) + liquid (4.0.3) + listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) + mercenary (0.3.6) + minima (2.5.0) + jekyll (~> 3.5) + jekyll-feed (~> 0.9) + jekyll-seo-tag (~> 2.1) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (3.0.3) + rb-fsevent (0.10.3) + rb-inotify (0.10.0) + ffi (~> 1.0) + rouge (3.3.0) + ruby_dep (1.5.0) + safe_yaml (1.0.5) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + +PLATFORMS + ruby + +DEPENDENCIES + jekyll (~> 3.8.5) + jekyll-feed (~> 0.6) + jekyll-redirect-from + jekyll-seo-tag + minima (~> 2.0) + +BUNDLED WITH + 2.0.1 diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 000000000..6bf1f516f --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,107 @@ +# Welcome to Jekyll! +# +# This config file is meant for settings that affect your whole blog, values +# which you are expected to set up once and rarely edit after that. If you find +# yourself editing this file very often, consider using Jekyll's data files +# feature for the data you need to update frequently. +# +# For technical reasons, this file is *NOT* reloaded automatically when you use +# 'bundle exec jekyll serve'. If you change this file, please restart the server process. + +# Site settings +# These are used to personalize your new site. If you look in the HTML files, +# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. +# You can create any custom variable you would like, and they will be accessible +# in the templates via {{ site.myvariable }}. +title: NextEPC +email: acetcom@gmail.com +description: >- # this means to ignore newlines until "baseurl:" + An open source implementation of the Evolved Packet Core of LTE networks + supporting 3GPP Release 13. +baseurl: "/blog" # the subpath of your site, e.g. /blog +url: "https://open5gs.org" # the base hostname & protocol for your site, e.g. http://example.com +author: "Sukchan Lee" +github_username: acetcom + +# Build settings +markdown: kramdown +plugins: + - jekyll-feed + - jekyll-seo-tag + - jekyll-redirect-from + +include: + - _pages + +# Exclude from processing. +# The following items will not be processed, by default. Create a custom list +# to override the default setting. +exclude: + - Gemfile + - Gemfile.lock + - node_modules + - vendor/bundle/ + - vendor/cache/ + - vendor/gems/ + - vendor/ruby/ + +# Minima date format +# refer to http://shopify.github.io/liquid/filters/date/ if you want to customize this +minima_reboot: + date_format: "%Y-%m-%d %H:%M" + +footer_icons: + - username: open5gs/nextepc + url: https://github.com/open5gs/nextepc + icon: icon-github.svg + +header_pages: + - _pages/docs.md + - _pages/faq.md + - _pages/about.md + - _pages/github.md + +show_excerpts: true + +# Collections +collections: + docs: + output: true + permalink: /:collection/:path/ + +defaults: + # _pages + - scope: + path: "" + type: pages + values: + layout: page + # _posts + - scope: + path: "" + type: posts + values: + layout: post + # _docs + - scope: + path: "" + type: docs + values: + layout: post + +# Archives +# Type +# - GitHub Pages compatible archive pages built with Liquid ~> type: liquid (default) +# - Jekyll Archives plugin archive pages ~> type: jekyll-archives +# Path (examples) +# - Archive page should exist at path when using Liquid method or you can +# expect broken links (especially with breadcrumbs enabled) +# - /tags/my-awesome-tag/index.html ~> path: /tags/ +# - path: /categories/ +# - path: / +category_archive: + type: liquid + path: /categories/ +tag_archive: + type: liquid + path: /tags/ diff --git a/docs/_docs/guide/01-quickstart.md b/docs/_docs/guide/01-quickstart.md new file mode 100644 index 000000000..b5710b5eb --- /dev/null +++ b/docs/_docs/guide/01-quickstart.md @@ -0,0 +1,233 @@ +--- +title: Qucikstart +--- + +**Note:** NextEPC supports installation of packages in *Debian/Ubuntu and openSUSE* environments. *CentOS, Fedora, FreeBSD, and Mac OSX* require you to [build with source code]({{ site.url }}{{ site.baseurl }}/docs/guide/02-building-nextepc-from-sources) +{: .notice--warning} + +### Install NextEPC with a Package Manager +--- + +The nextepc package is available on the recent versions of *Ubuntu*. + +```bash +$ sudo apt update +$ sudo apt install software-properties-common +$ sudo add-apt-repository ppa:acetcom/nextepc +$ sudo apt update +$ sudo apt install nextepc +``` + +>The NextEPC package is also available on [OBS](https://build.opensuse.org/package/show/home:acetcom:open5gs:snapshot/nextepc). First, install the authentication key as shown below. +```bash +$ sudo apt install wget +$ wget https://download.opensuse.org/repositories/home:/acetcom:/open5gs:/latest/xUbuntu_18.10/Release.key +$ sudo apt install gnupg +$ sudo apt-key add Release.key +``` +In Debian 9.0, you can install it as follows: +```bash +$ sudo sh -c "echo 'deb https://download.opensuse.org/repositories/home:/acetcom:/open5gs:/latest/Debian_9.0/ ./' > /etc/apt/sources.list.d/open5gs.list" +$ sudo apt update +$ sudo apt install nextepc +``` +Other Linux distributions can be installed by changing the path. +``` +https://download.opensuse.org/repositories/home:/acetcom:/open5gs:/latest/Raspbian_9.0/ +https://download.opensuse.org/repositories/home:/acetcom:/open5gs:/latest/xUbuntu_16.04/ +https://download.opensuse.org/repositories/home:/acetcom:/open5gs:/latest/xUbuntu_17.10/ +https://download.opensuse.org/repositories/home:/acetcom:/open5gs:/latest/xUbuntu_18.04/ +https://download.opensuse.org/repositories/home:/acetcom:/open5gs:/latest/xUbuntu_18.10/ +``` +{: .notice--success} + +[Martin Hauke](https://build.opensuse.org/user/show/mnhauke) packaged NextEPC for *openSUSE* on [OBS](https://build.opensuse.org/package/show/home:mnhauke:nextepc/nextepc). + +```bash +$ sudo zypper addrepo -f obs://home:mnhauke:nextepc home:mnhauke:nextepc +$ sudo zypper install nextepc +$ sudo zypper install mongodb-server mongodb-shell +``` + +### Configure NextEPC +--- + +Modify [/etc/nextepc/mme.conf](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/config/mme.conf.in) to set the S1AP/GTP-C IP address, PLMN ID, and TAC + +```diff +diff -u /etc/nextepc/mme.conf.old /etc/nextepc/mme.conf +--- mme.conf.old 2018-04-15 18:28:31.000000000 +0900 ++++ mme.conf 2018-04-15 19:53:10.000000000 +0900 +@@ -8,18 +8,20 @@ parameter: + mme: + freeDiameter: mme.conf + s1ap: ++ addr: 192.168.0.100 + gtpc: ++ addr: 192.168.0.100 + gummei: + plmn_id: +- mcc: 001 +- mnc: 01 ++ mcc: 901 ++ mnc: 70 + mme_gid: 2 + mme_code: 1 + tai: + plmn_id: +- mcc: 001 +- mnc: 01 +- tac: 12345 ++ mcc: 901 ++ mnc: 70 ++ tac: 7 + security: + integrity_order : [ EIA1, EIA2, EIA0 ] + ciphering_order : [ EEA0, EEA1, EEA2 ] +``` + +Modify [/etc/nextepc/sgw.conf](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/config/sgw.conf.in) to set the GTP-U IP address. +```diff +diff -u /etc/nextepc/sgw.conf.old /etc/nextepc/sgw.conf +--- sgw.conf.old 2018-04-15 18:30:25.000000000 +0900 ++++ sgw.conf 2018-04-15 18:30:30.000000000 +0900 +@@ -14,3 +14,4 @@ + gtpc: + addr: 127.0.0.2 + gtpu: ++ addr: 192.168.0.100 +``` + +After changing conf files, please restart NextEPC daemons. + +```bash +$ sudo systemctl restart nextepc-mmed +$ sudo systemctl restart nextepc-sgwd +``` + +### Install WebUI of NextEPC +--- + +[Node.js](https://nodejs.org/) is required to install WebUI of NextEPC + +1. *Debian and Ubuntu* based Linux distributions can install [Node.js](https://nodejs.org/) as follows: + + ```bash + $ sudo apt install curl + $ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - + $ sudo apt install nodejs + ``` + +2. To install [Node.js](https://nodejs.org/) on *openSUSE*, run the following: + + ```bash + $ sudo zypper install nodejs8 + ``` + +You can now install WebUI of NextEPC. + +```bash +$ curl -sL {{ site.url }}/static/webui/install | sudo -E bash - +``` + +### Register Subscriber Information +--- + +Connect to `http://localhost:3000` and login with **admin** account. + +> Username : admin +> Password : 1423 + +**Tip:** You can change the password in _Account_ Menu. +{: .notice--info} + +To add subscriber information, you can do WebUI operations in the following order: + + 1. Go to `Subscriber` Menu. + 2. Click `+` Button to add a new subscriber. + 3. Fill the IMSI, security context(K, OPc, AMF), and APN of the subscriber. + 4. Click `SAVE` Button + +**Tip:** This addition immediately affects NextEPC without restaring any daemon. +{: .notice--info} + + +### Adding a route for UE to have Internet connectivity +--- + +If your phone can connect to internet, you must run the following command in NextEPC-PGW installed host. + +```bash +$ sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward" +$ sudo iptables -t nat -A POSTROUTING -o 'interface-name' -j MASQUERADE +$ sudo iptables -I INPUT -i pgwtun -j ACCEPT +``` + +**Note:** In the above command, you should replace `'interface-name'` with your interface name that can connect to the internet. (For example, `enp0s25`, `wls3`, and so on). +{: .notice--danger} + +### Turn on your eNodeB and Phone +--- + +- You can see actual traffic through wireshark -- [[srsenb.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/srsenb.pcapng). +- You can view the log at `/var/log/nextepc/*.log`. + +### Troubleshooting +--- + +Problem with NextEPC can be filed as [GitHub Issues](https://github.com/open5gs/nextepc/issues). Please include the following to get help: + +- Attach `*.pcapng` file created by wireskark. +- Attach configuration files at `/etc/nextepc/*.conf`. +- Attach log files at `/var/log/nextepc/*.log`. + +You can modify the configuration file to record more logs. + +```diff +diff -u /etc/nextepc/mme.conf.old /etc/nextepc/mme.conf +--- mme.conf.old 2018-04-15 18:28:31.000000000 +0900 ++++ mme.conf 2018-04-15 19:53:10.000000000 +0900 +@@ -2,6 +2,7 @@ + + logger: + file: @LOCALSTATE_DIR@/log/nextepc/mme.log ++ level: debug + + parameter: +``` + +After changing conf files, please restart NextEPC daemons. + +```bash +$ sudo systemctl restart nextepc-mmed +$ sudo systemctl restart nextepc-sgwd +``` + +### Uninstall NextEPC and WebUI + +How to remove NextEPC package: + +1. On *Ubuntu*: + + ```bash + $ sudo apt purge nextepc* + ``` + +2. On *openSUSE*: + + ```bash + $ sudo zypper rm nextepc + ``` + +You may need to remove manually `/var/log/nextepc` unless it is empty. + +```bash +$ sudo rm -Rf /var/log/nextepc +``` + +The WebUI of NextEPC can be removed as follows: + +```bash +curl -sL {{ site.url }}{{ site.baseurl }}/assets/webui/uninstall | sudo -E bash - +``` + diff --git a/docs/_docs/guide/02-building-nextepc-from-sources.md b/docs/_docs/guide/02-building-nextepc-from-sources.md new file mode 100644 index 000000000..7bea74494 --- /dev/null +++ b/docs/_docs/guide/02-building-nextepc-from-sources.md @@ -0,0 +1,400 @@ +--- +title: Building nextepc from Sources +head_inline: "" +--- + +This post explains how to compile and install the source code on **Debian/Ubuntu** based Linux Distribution. +{: .blue} + +### Getting MongoDB +--- + +Install MongoDB with package manager. + +```bash +$ sudo apt update +$ sudo apt install mongodb +$ sudo systemctl start mongodb (if '/usr/bin/mongod' is not running) +``` + +### Setting up TUN device (No persistent after rebooting) +--- + +Create the TUN device with the interface name `pgwtun`. + +```bash +$ sudo ip tuntap add name pgwtun mode tun +$ sudo ip addr add 45.45.0.1/16 dev pgwtun +$ sudo ip addr add cafe::1/64 dev pgwtun +$ sudo ip link set pgwtun up +``` + +The script provided in [$GIT_REPO/support/network/restart.sh](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/network/restart.sh) makes it easy to configure the TUN device as follows: +`$ sudo ./support/network/restart.sh` +{: .notice--info} + +### Building NextEPC +--- + +Install the depedencies for building the source code. + +```bash +$ sudo apt install autoconf libtool gcc pkg-config git flex bison libsctp-dev libgnutls28-dev libgcrypt-dev libssl-dev libidn11-dev libmongoc-dev libbson-dev libyaml-dev +``` + +Git clone with `--recursive` option. + +```bash +➜ open5gs git clone --recursive https://github.com/{{ site.github_username }}/nextepc +``` + +To compile with autotools: + +```bash +➜ open5gs cd nextepc +➜ nextepc git:(master) ✗ autoreconf -iv +➜ nextepc git:(master) ✗ ./configure --prefix=`pwd`/install +➜ nextepc git:(master) ✗ make -j `nproc` +``` + +Check whether the compilation is correct. +```bash +➜ nextepc git:(master) ✗ make check +``` + +You need to perform the **installation process**. +```bash +➜ nextepc git:(master) ✗ make install +``` + +Check whether the installation is correct. +```bash +➜ nextepc git:(master) ✗ ./test/testcomplex +s1setup_test : SUCCESS +attach_test : SUCCESS +volte_test : SUCCESS +handover_test : SUCCESS +All tests passed. +``` + +**Tip:** You can also check the result of `./test/testcomplex` with a tool that captures packets. If you are running `wireshark`, select the `loopback` interface and set FILTER to `s1ap || gtpv2 || diameter || gtp`. You can see the virtually created packets. [[testcomplex.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/testcomplex.pcapng) +{: .notice--info} + +### Configure NextEPC +--- + +**Note:** In the developer environment, all settings can be managed in one place, such as [$INSTALL_PREFIX/install/etc/nextepc/nextepc.conf](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/config/nextepc.conf.in). +{: .notice--danger} + +Modify [$INSTALL_PREFIX/install/etc/nextepc/nextepc.conf](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/config/nextepc.conf.in) to set the S1AP/GTP-C/GTP-U IP address, PLMN ID, and TAC + +```diff +diff -u ./install/etc/nextepc/nextepc.conf.old ./install/etc/nextepc/nextepc.conf +--- nextepc.conf.old 2018-04-15 18:28:31.000000000 +0900 ++++ nextepc.conf 2018-04-15 19:53:10.000000000 +0900 +@@ -74,6 +74,7 @@ mme: + # dev: eth0 + # + s1ap: ++ addr: 192.168.0.100 + + # + # > +@@ -87,6 +88,7 @@ mme: + # - addr: ::1 + # + gtpc: ++ addr: 192.168.0.100 + + # + # +@@ -110,8 +112,8 @@ mme: + # + gummei: + plmn_id: +- mcc: 001 +- mnc: 01 ++ mcc: 901 ++ mnc: 70 + mme_gid: 2 + mme_code: 1 + +@@ -149,9 +151,9 @@ mme: + # + tai: + plmn_id: +- mcc: 001 +- mnc: 01 +- tac: 12345 ++ mcc: 901 ++ mnc: 70 ++ tac: 7 + + security: + integrity_order : [ EIA1, EIA2, EIA0 ] +@@ -242,6 +244,7 @@ sgw: + # gtpu: + # + gtpu: ++ addr: 192.168.0.100 + + pgw: + freeDiameter: pgw.conf +``` + +### Running NextEPC +--- + +For developers, it provides `nextepc-epcd` daemon that includes both *MME*, *SGW*, *PGW*, *HSS*, and *PCRF*. + +```bash +➜ nextepc git:(master) ✗ ./nextepc-epcd +04/06 23:13:03.367: [core] INFO: NextEPC daemon start (main.c:169) + +PID[6404]: '/home/acetcom/Documents/git/open5gs/nextepc/install/var/run/nextepc-epcd/pid' +File Logging: '/home/acetcom/Documents/git/open5gs/nextepc/install/var/log/nextepc/nextepc.log' +MongoDB URI: 'mongodb://localhost/nextepc' +Configuration: '/home/acetcom/Documents/git/open5gs/nextepc/install/etc/nextepc/nextepc.conf' +04/06 23:13:03.369: [core] INFO: PCRF try to initialize (epc.c:37) +... +``` + +Several command line options are provided. + +```bash +➜ nextepc git:(master) ✗ ./nextepc-epcd -h +Password: +NextEPC daemon v0.4.0.67-078c - Apr 6 2019 17:20:24 +Usage: ./nextepc-epcd [arguments] + +Arguments: + -v Show version + -h Show help + -D Start as daemon + -f Set configuration file name + -l log_file Log file path to be logged to + -p pid_file PID file path + -d core:gtp:event Enable debugging + -t sock:mem: Enable trace +``` + + +### Building WebUI of NextEPC +--- + +[Node.js](https://nodejs.org/) is required to build WebUI of NextEPC + +```bash +$ sudo apt install curl +$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - +$ sudo apt install nodejs +``` + +Install the dependencies to run WebUI + +```bash +➜ nextepc git:(master) ✗ cd webui +➜ webui git:(master) ✗ npm install +``` + +The WebUI runs as an [npm](https://www.npmjs.com/) script. + +```bash +➜ webui git:(master) ✗ npm run dev +``` + +### Register Subscriber Information +--- + +Connect to `http://localhost:3000` and login with **admin** account. + +> Username : admin +> Password : 1423 + +**Note:** +You can change the password in _Account_ Menu. +{: .notice--info} + +To add subscriber information, you can do WebUI operations in the following order: + + 1. Go to `Subscriber` Menu. + 2. Click `+` Button to add a new subscriber. + 3. Fill the IMSI, security context(K, OPc, AMF), and APN of the subscriber. + 4. Click `SAVE` Button + +**Tip:** This addition immediately affects NextEPC without restaring any daemon. +{: .notice--warning} + +### Adding a route for UE to have internet connectivity +--- + +If your phone can connect to internet, you must run the following command in NextEPC-PGW installed host. + +```bash +$ sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward" +$ sudo iptables -t nat -A POSTROUTING -o 'interface-name' -j MASQUERADE +$ sudo iptables -I INPUT -i pgwtun -j ACCEPT +``` + +**Note:** In the above command, you should replace `'interface-name'` with your interface name that can connect to the internet. (For example, `enp0s25`, `wls3`, and so on). +{: .notice--danger} + +### Turn on your eNodeB and Phone +--- + +- You can see actual traffic through wireshark -- [[srsenb.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/srsenb.pcapng). +- You can view the log at `$INSTALL_PREFIX/var/log/nextepc/*.log`. + +### Troubleshooting +--- + +Debugging tools can help you troubleshoot problems. + +- [GDB](https://www.gnu.org/software/gdb/) can be used as below: + + ```bash + ➜ nextepc git:(master) ✗ sudo apt install gdb + ➜ nextepc git:(master) ✗ ./libtool --mode=execute gdb ./nextepc-epcd + ``` + +- On *Mac OS X*, you can use the [LLDB](https://lldb.llvm.org/). + + ```bash + ➜ nextepc git:(master) ✗ ./libtool --mode=execute sudo lldb ./nextepc-epcd + ``` + +You can use the command line option[`-d`] to record more logs. + +```bash +➜ nextepc git:(master) ✗ ./nextepc-epcd -d +04/07 16:46:23.982: [core] INFO: NextEPC daemon start (main.c:169) + +PID[5185]: '/Users/acetcom/Documents/git/open5gs/nextepc/install/var/run/nextepc-epcd/pid' +File Logging: '/Users/acetcom/Documents/git/open5gs/nextepc/install/var/log/nextepc/nextepc.log' +MongoDB URI: 'mongodb://localhost/nextepc' +Configuration: '/Users/acetcom/Documents/git/open5gs/nextepc/install/etc/nextepc/nextepc.conf' +04/07 16:46:23.996: [core] INFO: PCRF try to initialize (epc.c:37) +04/07 16:46:24.033: [core] INFO: PCRF initialize...done (epc.c:40) +04/07 16:46:24.035: [core] INFO: PGW try to initialize (epc.c:84) +04/07 16:46:24.132: [thread] DEBUG: [0x10d4df908] thread started (ogs-thread.c:101) +04/07 16:46:24.132: [thread] DEBUG: [0x10d4df908] worker signal (ogs-thread.c:66) +04/07 16:46:24.132: [fd] INFO: CONNECTED TO 'pgw.localdomain' (TCP,soc#11): (fd_logger.c:113) +04/07 16:46:24.133: [core] INFO: PGW initialize...done (epc.c:87) +04/07 16:46:24.133: [pgw] DEBUG: pgw_state_initial(): INIT (pgw_sm.c:15) +04/07 16:46:24.133: [fd] INFO: CONNECTED TO 'pcrf.localdomain' (TCP,soc#11): (fd_logger.c:113) +04/07 16:46:24.134: [pgw] DEBUG: pgw_state_operational(): ENTRY (pgw_sm.c:33) +04/07 16:46:24.135: [core] INFO: SGW try to initialize (epc.c:133) +04/07 16:46:24.136: [sock] DEBUG: socket create(2:2:17) (ogs-socket.c:82) +04/07 16:46:24.136: [sock] DEBUG: udp socket(2) (ogs-udp.c:32) +04/07 16:46:24.137: [sock] DEBUG: socket bind 127.0.0.3:2123 (ogs-socket.c:107) +04/07 16:46:24.138: [sock] DEBUG: udp_server() [127.0.0.3]:2123 (ogs-udp.c:55) +04/07 16:46:24.139: [gtp] INFO: gtp_server() [127.0.0.3]:2123 (gtp_path.c:35) +04/07 16:46:24.140: [sock] DEBUG: socket create(2:2:17) (ogs-socket.c:82) +04/07 16:46:24.140: [sock] DEBUG: udp socket(2) (ogs-udp.c:32) +04/07 16:46:24.141: [sock] DEBUG: socket bind 127.0.0.3:2152 (ogs-socket.c:107) +04/07 16:46:24.142: [sock] DEBUG: udp_server() [127.0.0.3]:2152 (ogs-udp.c:55) +04/07 16:46:24.143: [gtp] INFO: gtp_server() [127.0.0.3]:2152 (gtp_path.c:35) +04/07 16:46:24.158: [thread] DEBUG: [0x10d4df408] worker signal (ogs-thread.c:66) +04/07 16:46:24.158: [thread] DEBUG: [0x10d4df408] thread started (ogs-thread.c:101) +04/07 16:46:24.159: [sgw] DEBUG: sgw_state_initial(): INIT + (sgw_sm.c:12) +04/07 16:46:24.159: [core] INFO: SGW initialize...done (epc.c:136) +04/07 16:46:24.160: [sgw] DEBUG: sgw_state_operational(): ENTRY + (sgw_sm.c:30) +04/07 16:46:24.161: [sock] DEBUG: socket create(2:2:17) (ogs-socket.c:82) +04/07 16:46:24.162: [sock] DEBUG: udp socket(2) (ogs-udp.c:32) +04/07 16:46:24.162: [sock] DEBUG: socket bind 127.0.0.2:2123 (ogs-socket.c:107) +04/07 16:46:24.162: [core] INFO: HSS try to initialize (epc.c:184) +04/07 16:46:24.163: [sock] DEBUG: udp_server() [127.0.0.2]:2123 (ogs-udp.c:55) +04/07 16:46:24.164: [gtp] INFO: gtp_server() [127.0.0.2]:2123 (gtp_path.c:35) +04/07 16:46:24.164: [sock] DEBUG: socket create(2:2:17) (ogs-socket.c:82) +04/07 16:46:24.165: [sock] DEBUG: udp socket(2) (ogs-udp.c:32) +04/07 16:46:24.166: [sock] DEBUG: socket bind 192.168.0.3:2152 (ogs-socket.c:107) +04/07 16:46:24.166: [sock] DEBUG: udp_server() [192.168.0.3]:2152 (ogs-udp.c:55) +04/07 16:46:24.167: [gtp] INFO: gtp_server() [192.168.0.3]:2152 (gtp_path.c:35) +04/07 16:46:24.254: [core] INFO: HSS initialize...done (epc.c:187) +04/07 16:46:24.255: [core] INFO: MME try to initialize (epc.c:217) +04/07 16:46:24.366: [fd] INFO: CONNECTED TO 'mme.localdomain' (TCP,soc#9): (fd_logger.c:113) +04/07 16:46:24.367: [fd] INFO: CONNECTED TO 'hss.localdomain' (TCP,soc#17): (fd_logger.c:113) +04/07 16:46:24.367: [thread] DEBUG: [0x10d4dfe08] thread started (ogs-thread.c:101) +04/07 16:46:24.367: [core] INFO: MME initialize...done (epc.c:220) +04/07 16:46:24.367: [thread] DEBUG: [0x10d4dfe08] worker signal (ogs-thread.c:66) + + +NextEPC daemon v0.4.0.67-078c - Apr 6 2019 17:20:24 + +04/07 16:46:24.368: [mme] DEBUG: mme_state_initial(): INIT + (mme_sm.c:23) +04/07 16:46:24.368: [mme] DEBUG: mme_state_operational(): ENTRY + (mme_sm.c:43) +04/07 16:46:24.368: [sock] DEBUG: socket create(2:2:17) (ogs-socket.c:82) +04/07 16:46:24.368: [sock] DEBUG: udp socket(2) (ogs-udp.c:32) +04/07 16:46:24.368: [sock] DEBUG: socket bind 192.168.0.3:2123 (ogs-socket.c:107) +04/07 16:46:24.368: [sock] DEBUG: udp_server() [192.168.0.3]:2123 (ogs-udp.c:55) +04/07 16:46:24.368: [gtp] INFO: gtp_server() [192.168.0.3]:2123 (gtp_path.c:35) +04/07 16:46:24.368: [sock] DEBUG: socket create(2:2:17) (ogs-socket.c:82) +04/07 16:46:24.368: [sock] DEBUG: udp socket(2) (ogs-udp.c:32) +04/07 16:46:24.368: [sock] DEBUG: socket connect 127.0.0.2:2123 + (ogs-socket.c:132) +04/07 16:46:24.368: [sock] DEBUG: udp_client() [127.0.0.2]:2123 (ogs-udp.c:89) +04/07 16:46:24.368: [gtp] INFO: gtp_client() [127.0.0.2]:2123 (gtp_path.c:49) +04/07 16:46:24.368: [mme] DEBUG: Old INITMSG (numout:10 maxin:2048 maxattempt:8 maxinit_to:60000) (s1ap_usrsctp.c:283) +04/07 16:46:24.368: [mme] DEBUG: New INITMSG (numout:30 maxin:65535 maxattempt:4 maxinit_to:8000) (s1ap_usrsctp.c:311) +04/07 16:46:24.368: [mme] INFO: s1ap_server() [192.168.0.3]:36412 (s1ap_usrsctp.c:69) +04/07 16:46:24.609: [pgw] DEBUG: [PGW] PROTO:17 SRC:2d2d0001 2d2d0001 d683d683 010f2296 (pgw_ipfw.c:277) +04/07 16:46:24.610: [pgw] DEBUG: [PGW] HLEN:20 DST:2d2d0001 d683d683 010f2296 0053756b (pgw_ipfw.c:280) +04/07 16:46:25.611: [pgw] DEBUG: [PGW] PROTO:17 SRC:2d2d0001 2d2d0001 d683d683 010f3a6d (pgw_ipfw.c:277) +04/07 16:46:25.612: [pgw] DEBUG: [PGW] HLEN:20 DST:2d2d0001 d683d683 010f3a6d 0053756b (pgw_ipfw.c:280) + +04/07 16:46:26.607: [pgw] DEBUG: [PGW] PROTO:17 SRC:2d2d0001 2d2d0001 d683d683 010fa451 (pgw_ipfw.c:277) +04/07 16:46:26.608: [pgw] DEBUG: [PGW] HLEN:20 DST:2d2d0001 d683d683 010fa451 0053756b (pgw_ipfw.c:280) +... +... +... +^C04/07 16:46:27.013: [core] INFO: SIGINT received (main.c:60) +04/07 16:46:27.013: [core] INFO: NextEPC daemon terminating... (main.c:185) +04/07 16:46:27.013: [core] INFO: DB-Client try to terminate (application.c:116) +04/07 16:46:27.019: [core] INFO: DB-Client terminate...done (application.c:118) +04/07 16:46:27.019: [core] INFO: MME try to terminate (epc.c:229) +04/07 16:46:27.019: [event] DEBUG: interrupt all (ogs-queue.c:260) +04/07 16:46:27.019: [thread] DEBUG: [0x10d4dfe08] thread running(1) (ogs-thread.c:111) +04/07 16:46:27.019: [mme] DEBUG: mme_state_operational(): EXIT + (mme_sm.c:43) +04/07 16:46:27.020: [mme] DEBUG: mme_state_final(): INIT + (mme_sm.c:32) +04/07 16:46:27.020: [thread] DEBUG: [0x10d4dfe08] worker done (ogs-thread.c:72) +04/07 16:46:27.021: [thread] DEBUG: [0x10d4dfe08] thread destroy (ogs-thread.c:123) +04/07 16:46:27.021: [thread] DEBUG: [0x10d4dfe08] thread join (ogs-thread.c:132) +04/07 16:46:27.021: [thread] DEBUG: [0x10d4dfe08] thread done (ogs-thread.c:138) +04/07 16:46:27.022: [fd] INFO: freeDiameter[6]: Initiating freeDiameter shutdown sequence (3) (fd_init.c:131) +04/07 16:46:27.148: [core] INFO: MME terminate...done (epc.c:231) +04/07 16:46:27.148: [core] INFO: HSS try to terminate (epc.c:194) +04/07 16:46:27.149: [fd] INFO: freeDiameter[6]: Initiating freeDiameter shutdown sequence (3) (fd_init.c:131) +04/07 16:46:27.257: [core] INFO: HSS terminate...done (epc.c:196) +04/07 16:46:27.257: [core] INFO: SGW try to terminate (epc.c:143) +04/07 16:46:27.258: [event] DEBUG: interrupt all (ogs-queue.c:260) +04/07 16:46:27.258: [thread] DEBUG: [0x10d4df408] thread running(1) (ogs-thread.c:111) +04/07 16:46:27.258: [sgw] DEBUG: sgw_state_operational(): EXIT + (sgw_sm.c:30) +04/07 16:46:27.259: [sgw] DEBUG: sgw_state_final(): INIT + (sgw_sm.c:21) +04/07 16:46:27.260: [thread] DEBUG: [0x10d4df408] worker done (ogs-thread.c:72) +04/07 16:46:27.260: [thread] DEBUG: [0x10d4df408] thread destroy (ogs-thread.c:123) +04/07 16:46:27.261: [thread] DEBUG: [0x10d4df408] thread join (ogs-thread.c:132) +04/07 16:46:27.261: [thread] DEBUG: [0x10d4df408] thread done (ogs-thread.c:138) +04/07 16:46:27.263: [core] INFO: SGW terminate...done (epc.c:145) +04/07 16:46:27.263: [core] INFO: PGW try to terminate (epc.c:94) +04/07 16:46:27.264: [event] DEBUG: interrupt all (ogs-queue.c:260) +04/07 16:46:27.265: [thread] DEBUG: [0x10d4df908] thread running(1) (ogs-thread.c:111) +04/07 16:46:27.265: [pgw] DEBUG: pgw_state_operational(): EXIT (pgw_sm.c:33) +04/07 16:46:27.266: [pgw] DEBUG: pgw_state_final(): INIT (pgw_sm.c:24) +04/07 16:46:27.267: [thread] DEBUG: [0x10d4df908] worker done (ogs-thread.c:72) +04/07 16:46:27.268: [thread] DEBUG: [0x10d4df908] thread destroy (ogs-thread.c:123) +04/07 16:46:27.269: [thread] DEBUG: [0x10d4df908] thread join (ogs-thread.c:132) +04/07 16:46:27.270: [thread] DEBUG: [0x10d4df908] thread done (ogs-thread.c:138) +04/07 16:46:27.271: [fd] INFO: freeDiameter[6]: Initiating freeDiameter shutdown sequence (3) (fd_init.c:131) +04/07 16:46:27.401: [core] INFO: PGW terminate...done (epc.c:96) +04/07 16:46:27.402: [core] INFO: PCRF try to terminate (epc.c:47) +04/07 16:46:27.403: [fd] INFO: freeDiameter[6]: Initiating freeDiameter shutdown sequence (3) (fd_init.c:131) +04/07 16:46:27.514: [core] INFO: PCRF terminate...done (epc.c:49) +``` diff --git a/docs/_docs/platform/01-debian-ubuntu.md b/docs/_docs/platform/01-debian-ubuntu.md new file mode 100644 index 000000000..7bb612865 --- /dev/null +++ b/docs/_docs/platform/01-debian-ubuntu.md @@ -0,0 +1,108 @@ +--- +title: Build on Ubuntu/Debian +--- + +### TUN device permissions +--- + +To run nextepc with least privilege, the TUN device privilege should be a `crw-rw-rw-`(666). Otherwise, you need to run nextepc daemon as root. If the permission is not `crw-rw-rw-`(666), you may need to install [udev](https://mirrors.edge.kernel.org/pub/linux/utils/kernel/hotplug/udev/udev.html) package. + +```bash +$ ls -al /dev/net/tun +crw-rw---- 1 root 28 10, 200 Feb 11 05:13 /dev/net/tun + +$ sudo apt install udev +$ sudo systemctl start systemd-udevd (if '/lib/systemd/systemd-udevd' is not running) + +$ ls -al /dev/net/tun +crw-rw-rw- 1 root 28 10, 200 Feb 11 05:13 /dev/net/tun +``` + +Nevertheless, if the permission do not change, you can run nextepc with root privileges or change the permission using [chmod](https://www.gnu.org/software/coreutils/manual/html_node/chmod-invocation.html) as follows: + +```bash +$ sudo chmod 666 /dev/net/tun +``` + +### Making TUN Permanent +--- + +Write a configuration file for the TUN deivce. +```bash +$ sudo sh -c "cat << EOF > /etc/systemd/network/99-nextepc.netdev +[NetDev] +Name=pgwtun +Kind=tun +EOF" +``` + +Create a TUN device. The interface name will be `pgwtun`. +```bash +$ sudo systemctl enable systemd-networkd +$ sudo systemctl restart systemd-networkd + +$ sudo apt install net-tools +$ ifconfig pgwtun +pgwtun: flags=4241 mtu 1500 + inet6 fe80::e86e:86d8:ea24:f8ee prefixlen 64 scopeid 0x20 + unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 2 bytes 255 (255.0 B) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 +``` + +Then, to support IPv6-enabled UEs, you must configure your TUN device to support IPv6. + +```bash +$ sysctl -n net.ipv6.conf.pgwtun.disable_ipv6 +1 + +$ sudo sh -c "echo 'net.ipv6.conf.pgwtun.disable_ipv6=0' > /etc/sysctl.d/30-nextepc.conf" +$ sudo sysctl -p /etc/sysctl.d/30-nextepc.conf + +$ sysctl -n net.ipv6.conf.pgwtun.disable_ipv6 +0 +``` + +**Note:** If your TUN device already supports IPv6, you can skip this steps above. +{: .notice--warning} + + +You are now ready to set the IP address on TUN device. + +```bash +$ sudo sh -c "cat << EOF > /etc/systemd/network/99-nextepc.network +[Match] +Name=pgwtun +[Network] +Address=45.45.0.1/16 +Address=cafe::1/64 +EOF" +``` + +Restart the TUN device + +```bash +$ sudo systemctl restart systemd-networkd +``` + +Make sure it is set up properly. + + +```bash +$ ifconfig pgwtun +pgwtun: flags=4305 mtu 1500 + inet 45.45.0.1 netmask 255.255.0.0 destination 45.45.0.1 + inet6 cafe::1 prefixlen 64 scopeid 0x0 + inet6 fe80::e86e:86d8:ea24:f8ee prefixlen 64 scopeid 0x20 + unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC) + RX packets 0 bytes 0 (0.0 B) + RX errors 0 dropped 0 overruns 0 frame 0 + TX packets 20 bytes 2019 (2.0 KB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 +``` + +**Note:** On *Linux*, you should run `nextepc-pgwd` first to correctly verify the IP address of TUN device. Otherwise, the TUN device's IP address is not displayed by the [ifconfig](http://net-tools.sourceforge.net/man/ifconfig.8.html) command. +{: .notice--warning} + diff --git a/docs/_docs/platform/02-centos.md b/docs/_docs/platform/02-centos.md new file mode 100644 index 000000000..a0162464a --- /dev/null +++ b/docs/_docs/platform/02-centos.md @@ -0,0 +1,145 @@ +--- +title: CentOS +head_inline: "" +--- + +This guide is based on **CentOS 7** Distribution. +{: .blue} + +### Getting MongoDB +--- + +Create the MongoDB repository file. +```bash +$ sudo sh -c 'cat << EOF > /etc/yum.repos.d/mongodb-org-3.4.repo +[mongodb-org-3.4] +name=MongoDB Repository +baseurl=https://repo.mongodb.org/yum/redhat/\$releasever/mongodb-org/3.4/x86_64/ +gpgcheck=1 +enabled=1 +gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc +EOF' +``` + +Install MongoDB with Package Manager. +```bash +sudo yum -y install mongodb-org +sudo systemctl start mongod (if '/usr/bin/mongod' is not running) +``` + +### Setting up TUN device (No persistent after rebooting) +--- + +Create the TUN device. Interface name will be `pgwtun`. +```bash +$ sudo yum -y install iproute +$ sudo ip tuntap add name pgwtun mode tun +$ ip link show +``` + +Then, to support IPv6-enabled UEs, you must configure your TUN device to support IPv6. + +```bash +$ sysctl -n net.ipv6.conf.pgwtun.disable_ipv6 +1 + +$ sudo -w net.ipv6.conf.pgwtun.disable_ipv6=0 + +$ sysctl -n net.ipv6.conf.pgwtun.disable_ipv6 +0 +``` + +**Note:** If your TUN device already supports IPv6, you can skip this steps above. +{: .notice--warning} + +You are now ready to set the IP address on TUN device. + +```bash +$ sudo ip addr add 45.45.0.1/16 dev pgwtun +$ sudo ip addr add cafe::1/64 dev pgwtun +``` + +Make sure it is set up properly. +```bash +$ sudo ip link set pgwtun up +$ ip link show +``` + +**Tip:** The script provided in [$GIT_REPO/support/network/restart.sh](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/network/restart.sh) makes it easy to configure the TUN device as follows: +`$ sudo ./support/network/restart.sh` +{: .notice--info} + +### Building NextEPC +--- + +Install the depedencies for building the source code. +```bash +$ sudo yum -y install git flex bison autoconf libtool lksctp-tools-devel libidn-devel gnutls-devel libgcrypt-devel openssl-devel cyrus-sasl-devel libyaml-devel +``` + +Configure EPEL package and install mongo-c-driver. +```bash +$ sudo yum -y install epel-release +$ sudo yum -y install mongo-c-driver-devel +``` + +Git clone with `--recursive` option. + +```bash +➜ open5gs git clone --recursive https://github.com/{{ site.github_username }}/nextepc +``` + +To compile with autotools: + +```bash +➜ open5gs cd nextepc +➜ nextepc git:(master) ✗ autoreconf -iv +➜ nextepc git:(master) ✗ ./configure --prefix=`pwd`/install +➜ nextepc git:(master) ✗ make -j `nproc` +``` + +Check whether the compilation is correct. +```bash +➜ nextepc git:(master) ✗ make check +``` + +You need to perform the **installation process**. +```bash +➜ nextepc git:(master) ✗ make install +``` + +Check whether the installation is correct. +```bash +➜ nextepc git:(master) ✗ ./test/testcomplex +s1setup_test : SUCCESS +attach_test : SUCCESS +volte_test : SUCCESS +handover_test : SUCCESS +All tests passed. +``` + +**Tip:** You can also check the result of `./test/testcomplex` with a tool that captures packets. If you are running `wireshark`, select the `loopback` interface and set FILTER to `s1ap || gtpv2 || diameter || gtp`. You can see the virtually created packets. [[testcomplex.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/testcomplex.pcapng) +{: .notice--info} + +### Building WebUI of NextEPC +--- + +[Node.js](https://nodejs.org/) is required to build WebUI of NextEPC + +```bash +$ curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash - +$ sudo yum -y install nodejs +``` + +Install the dependencies to run WebUI + +```bash +➜ nextepc git:(master) ✗ cd webui +➜ webui git:(master) ✗ npm install +``` + +The WebUI runs as an [npm](https://www.npmjs.com/) script. + +```bash +➜ webui git:(master) ✗ npm run dev +``` diff --git a/docs/_docs/platform/03-fedora.md b/docs/_docs/platform/03-fedora.md new file mode 100644 index 000000000..39f1bc6f0 --- /dev/null +++ b/docs/_docs/platform/03-fedora.md @@ -0,0 +1,131 @@ +--- +title: Fedora +head_inline: "" +--- + +This guide is based on **Fedora 27** Distribution. +{: .blue} + +### Getting MongoDB +--- + +Install MongoDB with package manager. +```bash +$ sudo dnf -y install mongodb-server +``` + +Run MongoDB server. +```bash +$ mkdir -p ./data/db +$ mongod --dbpath ./data/db +``` + +### Setting up TUN device (No persistent after rebooting) +--- + +Create the TUN device. Interface name will be `pgwtun`. +```bash +$ sudo dnf -y install iproute +$ sudo ip tuntap add name pgwtun mode tun +$ ip link show +``` + +Then, to support IPv6-enabled UEs, you must configure your TUN device to support IPv6. + +```bash +$ sysctl -n net.ipv6.conf.pgwtun.disable_ipv6 +1 + +$ sudo -w net.ipv6.conf.pgwtun.disable_ipv6=0 + +$ sysctl -n net.ipv6.conf.pgwtun.disable_ipv6 +0 +``` + +**Note:** If your TUN device already supports IPv6, you can skip this steps above. +{: .notice--warning} + +You are now ready to set the IP address on TUN device. + +```bash +$ sudo ip addr add 45.45.0.1/16 dev pgwtun +$ sudo ip addr add cafe::1/64 dev pgwtun +``` + +Make sure it is set up properly. +```bash +$ sudo ip link set pgwtun up +$ ip link show +``` + +**Tip:** The script provided in [$GIT_REPO/support/network/restart.sh](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/network/restart.sh) makes it easy to configure the TUN device as follows: +`$ sudo ./support/network/restart.sh` +{: .notice--info} + +### Building NextEPC +--- + +Install the depedencies for building the source code. +```bash +$ sudo dnf -y install git gcc flex bison autoconf libtool mongo-c-driver-devel lksctp-tools-devel libidn-devel gnutls-devel libgcrypt-devel openssl-devel cyrus-sasl-devel snappy-devel libyaml-devel +``` + +Git clone with `--recursive` option. + +```bash +➜ open5gs git clone --recursive https://github.com/{{ site.github_username }}/nextepc +``` + +To compile with autotools: + +```bash +➜ open5gs cd nextepc +➜ nextepc git:(master) ✗ autoreconf -iv +➜ nextepc git:(master) ✗ ./configure --prefix=`pwd`/install +➜ nextepc git:(master) ✗ make -j `nproc` +``` + +Check whether the compilation is correct. +```bash +➜ nextepc git:(master) ✗ make check +``` + +You need to perform **the installation process**. +```bash +➜ nextepc git:(master) ✗ make install +``` + +Check whether the installation is correct. +```bash +➜ nextepc git:(master) ✗ ./test/testcomplex +s1setup_test : SUCCESS +attach_test : SUCCESS +volte_test : SUCCESS +handover_test : SUCCESS +All tests passed. +``` + +**Tip:** You can also check the result of `./test/testcomplex` with a tool that captures packets. If you are running `wireshark`, select the `loopback` interface and set FILTER to `s1ap || gtpv2 || diameter || gtp`. You can see the virtually created packets. [[testcomplex.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/testcomplex.pcapng) +{: .notice--info} + +### Building WebUI of NextEPC +--- + +[Node.js](https://nodejs.org/) is required to build WebUI of NextEPC + +```bash +$ sudo dnf -y install nodejs +``` + +Install the dependencies to run WebUI + +```bash +➜ nextepc git:(master) ✗ cd webui +➜ webui git:(master) ✗ npm install +``` + +The WebUI runs as an [npm](https://www.npmjs.com/) script. + +```bash +➜ webui git:(master) ✗ npm run dev +``` diff --git a/docs/_docs/platform/04-freebsd.md b/docs/_docs/platform/04-freebsd.md new file mode 100644 index 000000000..7fb19de4c --- /dev/null +++ b/docs/_docs/platform/04-freebsd.md @@ -0,0 +1,134 @@ +--- +title: FreeBSD +head_inline: "" +--- + +This guide is based on **FreeBSD Relase 11.1**. +{: .blue} + +### Getting MongoDB +--- + +Install MongoDB with package manager. +```bash +$ sudo pkg install mongodb +``` + +Run MongoDB server. +```bash +$ mkdir -p ./data/db +$ mongod --dbpath ./data/db +``` + +### Setting up TUN device (No persistent after rebooting) +--- + +Configure the TUN device. +```bash +$ sudo ifconfig lo0 alias 127.0.0.2 netmask 255.255.255.255 +$ sudo ifconfig lo0 alias 127.0.0.3 netmask 255.255.255.255 +$ sudo ifconfig lo0 alias 127.0.0.4 netmask 255.255.255.255 +$ sudo ifconfig lo0 alias 127.0.0.5 netmask 255.255.255.255 +``` + +Enable IP forwarding +```bash +$ sudo sysctl -w net.inet.ip.forwarding=1 +``` + +**Tip:** The script provided in [$GIT_REPO/support/network/restart.sh](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/network/restart.sh) makes it easy to configure the TUN device as follows: +`$ sudo ./support/network/restart.sh` +{: .notice--info} + +### Building NextEPC +--- + +Install the depedencies for building the source code. +```bash +$ sudo pkg install git gcc bison gsed pkgconf autoconf automake libtool mongo-c-driver gnutls libgcrypt libidn libyaml +``` + +Git clone with `--recursive` option. + +```bash +➜ open5gs git clone --recursive https://github.com/{{ site.github_username }}/nextepc +``` + +To compile with autotools: + +```bash +➜ open5gs cd nextepc +➜ nextepc git:(master) ✗ autoreconf -iv +➜ nextepc git:(master) ✗ ./configure --prefix=`pwd`/install +➜ nextepc git:(master) ✗ make -j `nproc` +``` + +Check whether the compilation is correct. + +**Note:** This should require *sudo* due to access `/dev/tun0`. +{: .notice--danger} +```bash +➜ nextepc git:(master) ✗ sudo make check +``` + +You need to perform **the installation process**. +```bash +➜ nextepc git:(master) ✗ make install +``` + +Check whether the installation is correct. + +**Note:** This should require *sudo* due to access `/dev/tun0`. +{: .notice--danger} + +```bash +acetcom@nextepc:~/nextepc$ sudo ./test/testcomplex +s1setup_test : SUCCESS +attach_test : SUCCESS +volte_test : SUCCESS +handover_test : SUCCESS +All tests passed. +``` + +**Tip:** You can also check the result of `./test/testcomplex` with a tool that captures packets. If you are running `wireshark`, select the `loopback` interface and set FILTER to `s1ap || gtpv2 || diameter || gtp`. You can see the virtually created packets. [[testcomplex.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/testcomplex.pcapng) +{: .notice--info} + +For developers, it provides `nextepc-epcd` daemon that includes both *MME*, *SGW*, *PGW*, *HSS*, and *PCRF*. + +**Note:** This should require *sudo* due to access `/dev/tun0`. +{: .notice--danger} + +```bash +acetcom@nextepc:~/nextepc$ sudo ./nextepc-epcd +04/06 23:13:03.367: [core] INFO: NextEPC daemon start (main.c:169) + +PID[6404]: '/home/acetcom/Documents/git/open5gs/nextepc/install/var/run/nextepc-epcd/pid' +File Logging: '/home/acetcom/Documents/git/open5gs/nextepc/install/var/log/nextepc/nextepc.log' +MongoDB URI: 'mongodb://localhost/nextepc' +Configuration: '/home/acetcom/Documents/git/open5gs/nextepc/install/etc/nextepc/nextepc.conf' +04/06 23:13:03.369: [core] INFO: PCRF try to initialize (epc.c:37) +... +``` + +### Building WebUI of NextEPC +--- + +[Node.js](https://nodejs.org/) is required to build WebUI of NextEPC + +```bash +$ sudo pkg install node +``` + +Install the dependencies to run WebUI + +```bash +➜ nextepc git:(master) ✗ cd webui +➜ webui git:(master) ✗ npm install +``` + +The WebUI runs as an [npm](https://www.npmjs.com/) script. + +```bash +➜ webui git:(master) ✗ npm run dev +``` + diff --git a/docs/_docs/platform/05-macosx.md b/docs/_docs/platform/05-macosx.md new file mode 100644 index 000000000..ddca84608 --- /dev/null +++ b/docs/_docs/platform/05-macosx.md @@ -0,0 +1,141 @@ +--- +title: Mac OS X +head_inline: "" +--- + +This guide is based on **macOS High Sierra 10.13.3**. +{: .blue} + +### Installing Homebrew +--- + +```bash +$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +``` + +### Getting MongoDB +--- + +Install MongoDB with Package Manager. +```bash +$ brew install mongodb +``` + +Run MongoDB server. +```bash +$ mkdir -p ./data/db +$ mongod --dbpath ./data/db +``` + +### Setting up TUN device (No persistent after rebooting) +--- + +Install TUN/TAP driver +- You can download it from [http://tuntaposx.sourceforge.net/](http://tuntaposx.sourceforge.net/) + +Configure the TUN device. +```bash +$ sudo ifconfig lo0 alias 127.0.0.2 netmask 255.255.255.255 +$ sudo ifconfig lo0 alias 127.0.0.3 netmask 255.255.255.255 +$ sudo ifconfig lo0 alias 127.0.0.4 netmask 255.255.255.255 +$ sudo ifconfig lo0 alias 127.0.0.5 netmask 255.255.255.255 +``` + +Enable IP forwarding & Masquerading +```bash +$ sudo sysctl -w net.inet.ip.forwarding=1 +$ sudo sh -c "echo 'nat on {en0} from 45.45.0.0/16 to any -> {en0}' > /etc/pf.anchors/org.nextepc" +$ sudo pfctl -e -f /etc/pf.anchors/org.nextepc +``` + +**Tip:** The script provided in [$GIT_REPO/support/network/restart.sh](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/network/restart.sh) makes it easy to configure the TUN device as follows: +`$ sudo ./support/network/restart.sh` +{: .notice--info} + +### Building NextEPC +--- + +Install the depedencies for building the source code. +```bash +$ brew install autoconf automake libtool gnu-sed mongo-c-driver libusrsctp gnutls libgcrypt libidn libyaml pkg-config +``` + +Git clone with `--recursive` option. + +```bash +➜ open5gs git clone --recursive https://github.com/{{ site.github_username }}/nextepc +``` + +To compile with autotools: + +```bash +➜ open5gs cd nextepc +➜ nextepc git:(master) ✗ autoreconf -iv +➜ nextepc git:(master) ✗ ./configure --prefix=`pwd`/install +➜ nextepc git:(master) ✗ make -j `nproc` +``` + +**Note:** On MAC OS X, the compilation check is not supported at this point. +{: .notice--danger} + +You need to perform **the installation process**. +```bash +➜ nextepc git:(master) ✗ make install +``` + +Check whether the installation is correct. + +**Note:** This should require *sudo* due to access `/dev/tun0`. +{: .notice--danger} + +```bash +acetcom@nextepc:~/nextepc$ sudo ./test/testcomplex +s1setup_test : SUCCESS +attach_test : SUCCESS +volte_test : SUCCESS +handover_test : SUCCESS +All tests passed. +``` + +**Tip:** You can also check the result of `./test/testcomplex` with a tool that captures packets. If you are running `wireshark`, select the `loopback` interface and set FILTER to `s1ap || gtpv2 || diameter || gtp`. You can see the virtually created packets. [[testcomplex.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/testcomplex.pcapng) +{: .notice--info} + +For developers, it provides `nextepc-epcd` daemon that includes both *MME*, *SGW*, *PGW*, *HSS*, and *PCRF*. + +**Note:** This should require *sudo* due to access `/dev/tun0`. +{: .notice--danger} + +```bash +acetcom@nextepc:~/nextepc$ sudo ./nextepc-epcd +04/06 23:13:03.367: [core] INFO: NextEPC daemon start (main.c:169) + +PID[6404]: '/home/acetcom/Documents/git/open5gs/nextepc/install/var/run/nextepc-epcd/pid' +File Logging: '/home/acetcom/Documents/git/open5gs/nextepc/install/var/log/nextepc/nextepc.log' +MongoDB URI: 'mongodb://localhost/nextepc' +Configuration: '/home/acetcom/Documents/git/open5gs/nextepc/install/etc/nextepc/nextepc.conf' +04/06 23:13:03.369: [core] INFO: PCRF try to initialize (epc.c:37) +... +``` + +### Building WebUI of NextEPC +--- + +[Node.js](https://nodejs.org/) is required to build WebUI of NextEPC + +```bash +$ brew install node +``` + +Install the dependencies to run WebUI + +```bash +➜ nextepc git:(master) ✗ cd webui +➜ webui git:(master) ✗ npm install +``` + +The WebUI runs as an [npm](https://www.npmjs.com/) script. + +```bash +➜ webui git:(master) ✗ npm run dev +``` + diff --git a/docs/_docs/platform/06-ubuntu-trusty.md b/docs/_docs/platform/06-ubuntu-trusty.md new file mode 100644 index 000000000..0462e264d --- /dev/null +++ b/docs/_docs/platform/06-ubuntu-trusty.md @@ -0,0 +1,157 @@ +--- +title: Ubuntu (Trusty) +head_inline: "" +--- + +This guide is based on **Ubuntu 14.04(Trusty)** Distribution. +{: .blue} + +## Getting MongoDB + +Install MongoDB with package manager. +```bash +$ sudo apt update +$ sudo apt install mongodb +$ sudo systemctl start mongodb (if '/usr/bin/mongod' is not running) +``` + +### TUN device permissions +--- + +To run nextepc with least privilege, the TUN device privilege should be a `crw-rw-rw-`(666). Otherwise, you need to run nextepc daemon as root. If the permission is not `crw-rw-rw-`(666), you may need to install [udev](https://mirrors.edge.kernel.org/pub/linux/utils/kernel/hotplug/udev/udev.html) package. + +```bash +$ ls -al /dev/net/tun +crw-rw---- 1 root 28 10, 200 Feb 11 05:13 /dev/net/tun + +$ sudo apt install udev +$ sudo systemctl start systemd-udevd (if '/lib/systemd/systemd-udevd' is not running) + +$ ls -al /dev/net/tun +crw-rw-rw- 1 root 28 10, 200 Feb 11 05:13 /dev/net/tun +``` + +Nevertheless, if the permission do not change, you can run nextepc with root privileges or change the permission using [chmod](https://www.gnu.org/software/coreutils/manual/html_node/chmod-invocation.html) as follows: + +```bash +$ sudo chmod 666 /dev/net/tun +``` + +### Making TUN Permanent +--- + +Write the configuration file for the TUN deivce. +```bash +$ sudo sh -c "cat << EOF > /etc/network/interfaces.d/nextepc +auto pgwtun +iface pgwtun inet static + address 45.45.0.1 + netmask 255.255.0.0 + pre-up ip tuntap add name pgwtun mode tun + post-down ip tuntap del name pgwtun mode tun +iface pgwtun inet6 static + address cafe::1 + netmask 64 +EOF" +``` + +For loading TUN configuration, +```bash +$ sudo sh -c 'if ! grep "source-directory" /etc/network/interfaces | grep "/etc/network/interfaces.d" > /dev/null; then + echo "source-directory /etc/network/interfaces.d" >> /etc/network/interfaces +fi' +``` + +Create the TUN device. Interface name will be `pgwtun`. +```bash +$ ifup pgwtun +``` + +Then, to support IPv6-enabled UEs, you must configure your TUN device to support IPv6. + +```bash +$ sysctl -n net.ipv6.conf.pgwtun.disable_ipv6 +1 + +$ sudo sh -c "echo 'net.ipv6.conf.pgwtun.disable_ipv6=0' > /etc/sysctl.d/30-nextepc.conf" +$ sudo sysctl -p /etc/sysctl.d/30-nextepc.conf + +$ sysctl -n net.ipv6.conf.pgwtun.disable_ipv6 +0 +``` + +**Note:** If your TUN device already supports IPv6, you can skip this steps above. +{: .notice--warning} + + +### Building NextEPC +--- + +Install the depedencies for building the source code. + +```bash +sudo apt install git gcc flex bison make autoconf libtool pkg-config libsctp-dev libssl-dev libgnutls-dev libidn11-dev libyaml-dev +``` + +Then, compile and install Mongo C Driver like the followings. +```bash +sudo apt-get -y install g++ libsasl2-dev +tar xzf mongo-c-driver-1.8.0.tar.gz +cd mongo-c-driver-1.8.0 +./configure --disable-automatic-init-and-cleanup +make +sudo make install +sudo ldconfig +``` + +Git clone with `--recursive` option. + +```bash +➜ open5gs git clone --recursive https://github.com/{{ site.github_username }}/nextepc +``` + +To compile with autotools: + +```bash +➜ open5gs cd nextepc +➜ nextepc git:(master) ✗ autoreconf -iv +➜ nextepc git:(master) ✗ ./configure --prefix=`pwd`/install +➜ nextepc git:(master) ✗ make -j `nproc` +``` + +Check whether the compilation is correct. +```bash +➜ nextepc git:(master) ✗ make check +``` + +You need to perform **the installation process**. +```bash +➜ nextepc git:(master) ✗ make install +``` + +Check whether the installation is correct. +```bash +➜ nextepc git:(master) ✗ ./test/testcomplex +s1setup_test : SUCCESS +attach_test : SUCCESS +volte_test : SUCCESS +handover_test : SUCCESS +All tests passed. +``` + +**Tip:** You can also check the result of `./test/testcomplex` with a tool that captures packets. If you are running `wireshark`, select the `loopback` interface and set FILTER to `s1ap || gtpv2 || diameter || gtp`. You can see the virtually created packets. [[testcomplex.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/testcomplex.pcapng) +{: .notice--info} + +For developers, it provides `nextepc-epcd` daemon that includes both *MME*, *SGW*, *PGW*, *HSS*, and *PCRF*. + +```bash +➜ nextepc git:(master) ✗ ./nextepc-epcd +04/06 23:13:03.367: [core] INFO: NextEPC daemon start (main.c:169) + +PID[6404]: '/home/acetcom/Documents/git/open5gs/nextepc/install/var/run/nextepc-epcd/pid' +File Logging: '/home/acetcom/Documents/git/open5gs/nextepc/install/var/log/nextepc/nextepc.log' +MongoDB URI: 'mongodb://localhost/nextepc' +Configuration: '/home/acetcom/Documents/git/open5gs/nextepc/install/etc/nextepc/nextepc.conf' +04/06 23:13:03.369: [core] INFO: PCRF try to initialize (epc.c:37) +... +``` diff --git a/docs/_docs/tutorial/01-your-first-lte.md b/docs/_docs/tutorial/01-your-first-lte.md new file mode 100644 index 000000000..7fadc9985 --- /dev/null +++ b/docs/_docs/tutorial/01-your-first-lte.md @@ -0,0 +1,320 @@ +--- +title: Your First LTE +head_inline: "" +--- + +This post is the perfect starting point for learning to build your own LTE network. View this lession as a guided introduction -- including the installation, configuration, and best practices that will ease your learning. + +### Prerequisites +--- + +First, you have to prepare USRP B200/B210 to run srsENB. However, please keep in mind that you would still need a fairly high-end PC (at least dual-core i5, better quad-core i7) with USB 3.0 to attach the USRP B200/B210. + +Also, for USRP B200/B210 you will need a GPS antenna for clock synchronization. It is good to have a window near your desk where you can put the small GPS patch antenna. In my case, a 1 to 2 meters antenna cable is used between desk/computer and the window. + +For stable operation of USRP B200/B210, I used 10Mhz GPS-DO(GPS disciplined oscillator). Of course, a USIM card(sysmoUSIM-SJS1) was also inserted into the phone. + +### Overall Physical Setup +--- + +Setup your devices in the following order: + + 1. GPS antenna near window + 2. GPS antenna connected to "GPS ANT" connector of GPS-DO (SMA) + 3. 10MHz output (BNC) of GPS-DO connected to 10MHz input of USRP (SMA) + 4. GPS input of USRP open/unused! + 5. 1PPS input of USRP open/unused! + 6. 4x Small Antennas connected to USRP Rx/Tx ports (RF-A/RF-B) + 7. USRP powered via power supply or over USB + 8. USRP USB port connected to your PC + 9. GPS-DO powered via power supply + +**Note:** When the GPS-DO acquires a lock on the GPS signal, a **GREEN** LED is displayed. GPS takes time to function normally. You also need to wait for the **RED** LED(ALARM) to turn off. +{: .notice--warning} + +### Installation +--- + +We will use *Ubuntu 18.04(Bionic)* installed PC. +{: .blue .bold} + +#### 1. USRP Hardware Driver + +Most Linux distributions provide UHD as part of their package management. On *Debian and Ubuntu* systems, this will install the base UHD library, all headers and build-specific files, as well as utilities: + +```bash +$ sudo add-apt-repository ppa:ettusresearch/uhd +$ sudo apt update +$ sudo apt install libuhd-dev libuhd003 uhd-host +``` + +After installing, you need to download the FPGA images packages by running _uhd images downloader_ on the command line (the actual path may differ based on your installation): + +```bash +$ sudo /usr/lib/uhd/utils/uhd_images_downloader.py +``` + +#### 2. srsENB + +On *Ubuntu 18.04(Bionic)*, one can install the required libraries with: + +```bash +$ sudo apt install cmake libfftw3-dev libmbedtls-dev libboost-program-options-dev libconfig++-dev libsctp-dev +``` + +Download and build srsLTE: + +```bash +➜ git git clone https://github.com/srsLTE/srsLTE.git +➜ git cd srsLTE +➜ srsLTE git:(master) ✗ mkdir build +➜ srsLTE git:(master) ✗ cd build +➜ build git:(master) ✗ cmake ../ +➜ build git:(master) ✗ make +➜ build git:(master) ✗ make test +``` + +#### 3. NextEPC + +The NextEPC package is available on the recent versions of *Ubuntu*. + +```bash +# Getting the authentication key +$ sudo apt install wget +$ wget https://download.opensuse.org/repositories/home:/acetcom:/open5gs:/latest/xUbuntu_18.04/Release.key +$ sudo apt-key add Release.key + +# Installing NextEPC +$ sudo sh -c "echo 'deb https://download.opensuse.org/repositories/home:/acetcom:/open5gs:/latest/xUbuntu_18.04/ ./' > /etc/apt/sources.list.d/open5gs.list" +$ sudo apt update +$ sudo apt install nextepc +``` + +The following shows how to install the Web UI of NextEPC. + +```bash +$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - +$ sudo apt install nodejs +$ curl -sL http://nextepc.org/static/webui/install | sudo -E bash - +``` + +### Configuration & Running +--- + +#### 1. NextEPC + +When you purchase the sysmoUSIM, you will receive the following information via e-mail. + +``` +Title : sysmocom SIM Card Details / AM93\PICK\00859 + +IMSI ICCID ACC PIN1 PUK1 PIN2 PUK2 Ki OPC ADM1 KIC1 KID1 KIK1 +... +901700000017408 8988211000000174089 0100 3623 84724035 8774 57473966 B1233463AB9BC2AD2DB1830EB6417E7B 625150E2A943E3353DD23554101CAFD4 47190711 C865CAA0A54542333929B29B116F4375 7D7F65DCD99003C0A0D5D31CA3E5253E 5B27983AF628FC3FCB36B89300012944 +``` + +Here's my subscriber information from above. + +``` +IMSI : 901700000017408 +K : B1233463AB9BC2AD2DB1830EB6417E7B +OPc : 625150E2A943E3353DD23554101CAFD4 +``` + +Connect to `http://localhost:3000` and login with **admin** account. + +> Username : admin +> Password : 1423 + +Then proceed as follows: + + 1. Go to `Subscriber` Menu. + 2. Click `+` Button to add a new subscriber. + 3. Fill the IMSI, security context(K, OPc, AMF), and APN of the subscriber. + 4. Click `SAVE` Button + +Modify [/etc/nextepc/mme.conf](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/config/mme.conf.in) to set the S1AP/GTP-C IP address, PLMN ID, and TAC + +```diff +diff -u mme.conf.old mme.conf +--- mme.conf.old 2018-04-15 18:28:31.000000000 +0900 ++++ mme.conf 2018-04-15 19:53:10.000000000 +0900 +@@ -14,18 +14,20 @@ + mme: + freeDiameter: mme.conf + s1ap: ++ addr: 127.0.1.100 + gtpc: ++ addr: 127.0.1.100 + gummei: + plmn_id: +- mcc: 001 +- mnc: 01 ++ mcc: 901 ++ mnc: 70 + mme_gid: 2 + mme_code: 1 + tai: + plmn_id: +- mcc: 001 +- mnc: 01 +- tac: 12345 ++ mcc: 901 ++ mnc: 70 ++ tac: 7 + security: + integrity_order : [ EIA1, EIA2, EIA0 ] + ciphering_order : [ EEA0, EEA1, EEA2 ] +``` + +S1AP/GTP-C IP address, PLMN ID, TAC are changed as follows. + +``` +S1AP address : 127.0.1.100 - srsENB default value +GTP-C address : 127.0.1.100 - Use loopback interface +PLMN ID : MNC(901), MCC(70) - sysmoUSIM default value +TAC : 7 - srsENB default value +``` + + +The GTP-U IP address will be set to 127.0.0.2. To do this, modify [/etc/nextepc/sgw.conf](https://github.com/{{ site.github_username }}/nextepc/blob/master/support/config/sgw.conf.in) to set the GTP-U IP address. + +```diff +diff -u /etc/nextepc/sgw.conf.old /etc/nextepc/sgw.conf +--- sgw.conf.old 2018-04-15 18:30:25.000000000 +0900 ++++ sgw.conf 2018-04-15 18:30:30.000000000 +0900 +@@ -14,3 +14,4 @@ + gtpc: + addr: 127.0.0.2 + gtpu: ++ addr: 127.0.0.2 +``` + +After changing conf files, please restart NextEPC daemons. + +```bash +$ sudo systemctl restart nextepc-mmed +$ sudo systemctl restart nextepc-sgwd +``` + +If your phone can connect to internet, you must run the following command in NextEPC-PGW installed host. + +```bash +$ sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward" +$ sudo iptables -t nat -A POSTROUTING -o 'interface-name' -j MASQUERADE +$ sudo iptables -I INPUT -i pgwtun -j ACCEPT +``` + +**Note:** In the above command, you should replace `'interface-name'` with your interface name that can connect to the internet. (For example, `enp0s25`, `wls3`, and so on). +{: .notice--danger} + +#### 2. srsENB +Change back to the srsENB source directory and copy the main config example as well as all additional config files for RR, SIB and DRB. + +```bash +➜ srsLTE git:(master) ✗ cp srsenb/enb.conf.example srsenb/enb.conf +➜ srsLTE git:(master) ✗ cp srsenb/rr.conf.example srsenb/rr.conf +➜ srsLTE git:(master) ✗ cp srsenb/sib.conf.example srsenb/sib.conf +➜ srsLTE git:(master) ✗ cp srsenb/drb.conf.example srsenb/drb.conf +``` + +You should check your phone frequency. If your phone does not support Band-3, you should use a different DL EARFCN value. + +```diff +--- enb.conf.example 2018-11-19 18:16:06.953631893 +0900 ++++ enb.conf 2019-04-08 11:15:18.051261318 +0900 +@@ -23,8 +23,8 @@ + cell_id = 0x01 + phy_cell_id = 1 + tac = 0x0007 +-mcc = 001 +-mnc = 01 ++mcc = 901 ++mnc = 70 + mme_addr = 127.0.1.100 + gtp_bind_addr = 127.0.1.1 + s1c_bind_addr = 127.0.1.1 +@@ -66,12 +66,13 @@ + # Default "auto". B210 USRP: 400 us, bladeRF: 0 us. + ##################################################################### + [rf] +-dl_earfcn = 3400 ++dl_earfcn = 1600 + tx_gain = 80 + rx_gain = 40 + + #device_name = auto + #device_args = auto ++device_args="clock=external" + #time_adv_nsamples = auto + #burst_preamble_us = auto +``` + +PLMN ID, DL EARFCN, and Device Argument are updated as belows. + +``` +PLMN ID : MNC(901), MCC(70) sysmoUSIM default value +DL EARFCN : Band-3 - from your Phone +Device Argument : Clock source from external GPS-DO +``` + +Now, run the srsENB as follows: + +```bash +➜ srsLTE git:(master) ✗ cd srsenb/ +➜ srsenb git:(master) ✗ sudo ../build/srsenb/src/srsenb ./enb.conf +linux; GNU C++ version 6.2.0 20161027; Boost_106200; UHD_003.009.005-0-unknow + +--- Software Radio Systems LTE eNodeB --- + +Reading configuration file ./enb.conf... +-- Loading firmware image: /usr/share/uhd/images/usrp_b200_fw.hex... +Opening USRP with args: "",master_clock_rate=30.72e6 +-- Detected Device: B200 +-- Loading FPGA image: /usr/share/uhd/images/usrp_b200_fpga.bin... done +-- Operating over USB 3. +-- Detecting internal GPSDO.... 'No GPSDO found' +-- Initialize CODEC control... +-- Initialize Radio control... +-- Performing register loopback test... pass +-- Performing CODEC loopback test... pass +-- Asking for clock rate 30.720000 MHz... +-- Actually got clock rate 30.720000 MHz. +-- Performing timer loopback test... pass +Setting frequency: DL=1845.0 Mhz, UL=1750.0 MHz +Setting Sampling frequency 11.52 MHz + +==== eNodeB started === +Type to view trace +``` + +If you see the `No GPSDO found`, please exit the program with Ctrl-C. +The following console output is the correct result of srsENB. +```bash +linux; GNU C++ version 6.2.0 20161027; Boost_106200; UHD_003.009.005-0-unknow + +--- Software Radio Systems LTE eNodeB --- + +Reading configuration file ./enb.conf... +Opening USRP with args: "",master_clock_rate=30.72e6 +-- Detected Device: B200 +-- Operating over USB 3. +-- Initialize CODEC control... +-- Initialize Radio control... +-- Performing register loopback test... pass +-- Performing CODEC loopback test... pass +-- Asking for clock rate 30.720000 MHz... +-- Actually got clock rate 30.720000 MHz. +-- Performing timer loopback test... pass +Setting frequency: DL=1845.0 Mhz, UL=1750.0 MHz +Setting Sampling frequency 11.52 MHz + +==== eNodeB started === +Type to view trace +``` + +### Turn on your eNodeB and Phone +--- + +- You can see actual traffic through wireshark -- [[srsenb.pcapng]]({{ site.url }}{{ site.baseurl }}/assets/pcapng/srsenb.pcapng). +- You can view the log at `/var/log/nextepc/*.log`. diff --git a/docs/_includes/disqus_comments.html b/docs/_includes/disqus_comments.html new file mode 100644 index 000000000..87fa30942 --- /dev/null +++ b/docs/_includes/disqus_comments.html @@ -0,0 +1,20 @@ +{% if page.comments != false and jekyll.environment == "production" %} + +
+ + +{% endif %} diff --git a/docs/_includes/footer.html b/docs/_includes/footer.html new file mode 100644 index 000000000..e253e21f2 --- /dev/null +++ b/docs/_includes/footer.html @@ -0,0 +1,42 @@ + diff --git a/docs/_includes/google-analytics.html b/docs/_includes/google-analytics.html new file mode 100644 index 000000000..9dffa1418 --- /dev/null +++ b/docs/_includes/google-analytics.html @@ -0,0 +1,10 @@ + diff --git a/docs/_includes/head-includes.html b/docs/_includes/head-includes.html new file mode 100644 index 000000000..e69de29bb diff --git a/docs/_includes/head.html b/docs/_includes/head.html new file mode 100644 index 000000000..c8591daed --- /dev/null +++ b/docs/_includes/head.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + {% if page.head_inline %} + {{ page.head_inline }} + {% endif %} + + {% if jekyll.environment == 'production' and site.google_analytics %} + {% include google-analytics.html %} + {% endif %} + + {% seo %} + + {% include head-includes.html %} + diff --git a/docs/_includes/header.html b/docs/_includes/header.html new file mode 100644 index 000000000..450e3d23c --- /dev/null +++ b/docs/_includes/header.html @@ -0,0 +1,24 @@ + diff --git a/docs/_includes/icon-github.svg b/docs/_includes/icon-github.svg new file mode 100644 index 000000000..83549f76a --- /dev/null +++ b/docs/_includes/icon-github.svg @@ -0,0 +1,4 @@ + +GitHub + + diff --git a/docs/_layouts/allposts.html b/docs/_layouts/allposts.html new file mode 100644 index 000000000..2dea94027 --- /dev/null +++ b/docs/_layouts/allposts.html @@ -0,0 +1,39 @@ +--- +layout: page +--- + +{{ content }} + +{% for post in site.posts %} + + {% capture year %}{{ post.date | date: '%Y' }}{% endcapture %} + + {% if forloop.first %} +

{{ year }}

+ + {% elsif year != prev_year %} +
+

{{ year }}

+ + {% endif %} + + {% assign date_format = site.minima_reboot.date_format_short | default: "%b %-d" %} + + + + + + + {% if forloop.last %} +
|{{ post.title | escape }}
+ {% endif %} + + {% capture prev_year %}{{ year }}{% endcapture %} + +{% endfor %} + +
+ +{% if site.plugins contains "jekyll-feed" %} +
+{% endif %} diff --git a/docs/_layouts/compress.html b/docs/_layouts/compress.html new file mode 100644 index 000000000..3fd067a8d --- /dev/null +++ b/docs/_layouts/compress.html @@ -0,0 +1,10 @@ +--- +# Jekyll layout that compresses HTML +# v3.0.4 +# http://jch.penibelst.de/ +# © 2014–2015 Anatol Broder +# MIT License +--- + +{% capture _LINE_FEED %} +{% endcapture %}{% if site.compress_html.ignore.envs contains jekyll.environment %}{{ content }}{% else %}{% capture _content %}{{ content }}{% endcapture %}{% assign _profile = site.compress_html.profile %}{% if site.compress_html.endings == "all" %}{% assign _endings = "html head body li dt dd optgroup option colgroup caption thead tbody tfoot tr td th" | split: " " %}{% else %}{% assign _endings = site.compress_html.endings %}{% endif %}{% for _element in _endings %}{% capture _end %}{% endcapture %}{% assign _content = _content | remove: _end %}{% endfor %}{% if _profile and _endings %}{% assign _profile_endings = _content | size | plus: 1 %}{% endif %}{% for _element in site.compress_html.startings %}{% capture _start %}<{{ _element }}>{% endcapture %}{% assign _content = _content | remove: _start %}{% endfor %}{% if _profile and site.compress_html.startings %}{% assign _profile_startings = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.comments == "all" %}{% assign _comments = "" | split: " " %}{% else %}{% assign _comments = site.compress_html.comments %}{% endif %}{% if _comments.size == 2 %}{% capture _comment_befores %}.{{ _content }}{% endcapture %}{% assign _comment_befores = _comment_befores | split: _comments.first %}{% for _comment_before in _comment_befores %}{% if forloop.first %}{% continue %}{% endif %}{% capture _comment_outside %}{% if _carry %}{{ _comments.first }}{% endif %}{{ _comment_before }}{% endcapture %}{% capture _comment %}{% unless _carry %}{{ _comments.first }}{% endunless %}{{ _comment_outside | split: _comments.last | first }}{% if _comment_outside contains _comments.last %}{{ _comments.last }}{% assign _carry = false %}{% else %}{% assign _carry = true %}{% endif %}{% endcapture %}{% assign _content = _content | remove_first: _comment %}{% endfor %}{% if _profile %}{% assign _profile_comments = _content | size | plus: 1 %}{% endif %}{% endif %}{% assign _pre_befores = _content | split: "" %}{% assign _pres_after = "" %}{% if _pres.size != 0 %}{% if site.compress_html.blanklines %}{% assign _lines = _pres.last | split: _LINE_FEED %}{% capture _pres_after %}{% for _line in _lines %}{% assign _trimmed = _line | split: " " | join: " " %}{% if _trimmed != empty or forloop.last %}{% unless forloop.first %}{{ _LINE_FEED }}{% endunless %}{{ _line }}{% endif %}{% endfor %}{% endcapture %}{% else %}{% assign _pres_after = _pres.last | split: " " | join: " " %}{% endif %}{% endif %}{% capture _content %}{{ _content }}{% if _pre_before contains "" %}{% endif %}{% unless _pre_before contains "" and _pres.size == 1 %}{{ _pres_after }}{% endunless %}{% endcapture %}{% endfor %}{% if _profile %}{% assign _profile_collapse = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.clippings == "all" %}{% assign _clippings = "html head title base link meta style body article section nav aside h1 h2 h3 h4 h5 h6 hgroup header footer address p hr blockquote ol ul li dl dt dd figure figcaption main div table caption colgroup col tbody thead tfoot tr td th" | split: " " %}{% else %}{% assign _clippings = site.compress_html.clippings %}{% endif %}{% for _element in _clippings %}{% assign _edges = " ;; ;" | replace: "e", _element | split: ";" %}{% assign _content = _content | replace: _edges[0], _edges[1] | replace: _edges[2], _edges[3] | replace: _edges[4], _edges[5] %}{% endfor %}{% if _profile and _clippings %}{% assign _profile_clippings = _content | size | plus: 1 %}{% endif %}{{ _content }}{% if _profile %}
Step Bytes
raw {{ content | size }}{% if _profile_endings %}
endings {{ _profile_endings }}{% endif %}{% if _profile_startings %}
startings {{ _profile_startings }}{% endif %}{% if _profile_comments %}
comments {{ _profile_comments }}{% endif %}{% if _profile_collapse %}
collapse {{ _profile_collapse }}{% endif %}{% if _profile_clippings %}
clippings {{ _profile_clippings }}{% endif %}
{% endif %}{% endif %} diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html new file mode 100644 index 000000000..d6eb2ad30 --- /dev/null +++ b/docs/_layouts/default.html @@ -0,0 +1,23 @@ +--- +layout: compress +--- + + + + {% include head.html %} + + + + {% include header.html %} + +
+
+ {{ content }} +
+
+ + {% include footer.html %} + + + + diff --git a/docs/_layouts/home.html b/docs/_layouts/home.html new file mode 100644 index 000000000..dc1265c57 --- /dev/null +++ b/docs/_layouts/home.html @@ -0,0 +1,52 @@ +--- +layout: default +--- + +
+ {{ content }} +
+ +
+ + {% if site.paginate %} + {% assign posts = paginator.posts %} + {% else %} + {% assign posts = site.posts %} + {% endif %} + +
    + {% assign date_format = site.minima_reboot.date_format | default: "%b %-d, %Y" %} + {% for post in posts %} +
  • + {{ post.date | date: date_format }} + +

    + {{ post.title | escape }} +

    + +
    + {{ post.excerpt }} +
    +
  • + {% endfor %} +
+ + {% if site.paginate %} + + {% endif %} + + {% if site.plugins contains "jekyll-feed" %} +
+ {% endif %} + +
diff --git a/docs/_layouts/page.html b/docs/_layouts/page.html new file mode 100644 index 000000000..0dcd6ceef --- /dev/null +++ b/docs/_layouts/page.html @@ -0,0 +1,14 @@ +--- +layout: default +--- +
+ +
+

{{ page.title | escape }}

+
+ +
+ {{ content }} +
+ +
diff --git a/docs/_layouts/post.html b/docs/_layouts/post.html new file mode 100644 index 000000000..b55dfba31 --- /dev/null +++ b/docs/_layouts/post.html @@ -0,0 +1,33 @@ +--- +layout: default +--- +
+ +
+

{{ page.title | escape }}

+

+ {% if page.author %} + {% if page.author_url %} + + {% endif %} + + {% if page.author_url %} + + {% endif %} + • + {% endif %} + +

+
+ +
+ {{ content }} +
+ + {% if site.disqus.shortname %} + {% include disqus_comments.html %} + {% endif %} +
diff --git a/docs/_pages/404.html b/docs/_pages/404.html new file mode 100644 index 000000000..5e0616e3c --- /dev/null +++ b/docs/_pages/404.html @@ -0,0 +1,25 @@ +--- +layout: default +permalink: /404.html +--- + + + +
+

404

+ +

Page not found :(

+

The requested page could not be found.

+
diff --git a/docs/_pages/about.md b/docs/_pages/about.md new file mode 100644 index 000000000..4bcd3736e --- /dev/null +++ b/docs/_pages/about.md @@ -0,0 +1,29 @@ +--- +layout: page +title: About +permalink: /about/ +--- + +NextEPC is a C-language Open Source implementation of the 3GPP Evolved Packet Core, i.e. the core network of an LTE network. + +#### Supported Features +--- + +- LTE release 14 complient +- AES, Snow3G, ZUC algorithms for encryption +- Support of USIM cards using Milenage +- Multiple PDN support +- S1/X2 Handover +- IPv6 support +- Supports several IMS servers with Rx interface. + +#### Known Limitations +--- + +- No OCS/OFCS +- No NB-IoT +- No eMBMS +- No CS Fallback and SRVCC +- No Roaming +- No Emergency Call +- No ePDG Gateway diff --git a/docs/_pages/docs.md b/docs/_pages/docs.md new file mode 100644 index 000000000..6091bbd46 --- /dev/null +++ b/docs/_pages/docs.md @@ -0,0 +1,21 @@ +--- +layout: page +title: Documentation +permalink: /docs/ +head_inline: "" +--- + +- User's Guide 2 + - [Quickstart](guide/01-quickstart) + - [Building nextepc from Sources](guide/02-building-nextepc-from-sources) + +- Tutorials + - [Your First LTE](tutorial/01-your-first-lte) + +- Platform Specific Notes + - [Debian/Ubuntu](platform/01-debian-ubuntu) + - [CentOS](platform/02-centos) + - [Fedora](platform/03-fedora) + - [FreeBSD](platform/04-freebsd) + - [MacOSX](platform/05-macosx) + - [Ubuntu(Trusty)](platform/06-ubuntu-trusty) diff --git a/docs/_pages/faq.md b/docs/_pages/faq.md new file mode 100644 index 000000000..4bf012df4 --- /dev/null +++ b/docs/_pages/faq.md @@ -0,0 +1,293 @@ +--- +layout: page +title: FAQ +permalink: /faq/ +--- + +#### How to connect MongoDB server + +If you do not start MongoDB, you will get the following error: + +```bash +➜ nextepc git:(master) ✗ ./test/testepc +04/09 15:42:34.817: [core] ERROR: Failed to conect to server [mongodb://localhost/nextepc] (context.c:326) +04/09 15:42:34.817: [core] ERROR: app_initialize() failed (basic/abts-main.c:91) +s1ap_message_test : -04/09 15:42:34.830: [s1ap] ERROR: Failed to decode S1AP-PDU[-1] (s1ap_encoder.c:41) +04/09 15:42:34.830: [core] FATAL: ogs_log_vprintf: Assertion `domain' failed. (ogs-log.c:347) +``` + +You can start MongoDB using systemctl. +```bash +$ sudo systemctl start mongodb +``` + +#### Failing to run `./nextepc-epcd` + +You might be getting the following error after running `./nextepc-epcd`. +```bash +➜ nextepc git:(master) ./nextepc-epcd +04/09 15:41:02.600: [core] INFO: NextEPC daemon start (main.c:169) +04/09 15:41:02.601: [core] ERROR: CHECK PERMISSION of Installation Directory... (application.c:144) +04/09 15:41:02.601: [core] ERROR: Cannot create PID file:`/home/acetcom/Documents/git/open5gs/nextepc/install/var/run/nextepc-epcd/pid` (application.c:145) +04/09 15:41:02.601: [core] WARNING: log_pid: should not be reached. (application.c:146) +/home/acetcom/Documents/git/open5gs/nextepc/lib/ogslib/src/core/.libs/libogscore-1.0.so.0(ogs_abort+0x2b)[0x7f9d5d26d71b] +/home/acetcom/Documents/git/open5gs/nextepc/.libs/nextepc-epcd(+0x9606)[0x563a4ba23606] +/home/acetcom/Documents/git/open5gs/nextepc/.libs/nextepc-epcd(+0x8640)[0x563a4ba22640] +/home/acetcom/Documents/git/open5gs/nextepc/.libs/nextepc-epcd(+0x81f3)[0x563a4ba221f3] +[1] 9635 abort (core dumped) ./nextepc-epcd +``` + +You should perform **the installation process**. + +```bash +$ make install +``` + +#### I have some error when running `./test/testepc` + +Did you see the following error after executing `testepc`? +```bash +➜ nextepc git:(master) ✗ ./test/testepc +s1ap_message_test : SUCCESS +nas_message_test : SUCCESS +gtp_message_test : SUCCESS +security_test : SUCCESS +s1setup_test : SUCCESS +attach_test : -Line 134: Condition is false, but expected true +\04/09 15:49:09.285: [esm] FATAL: esm_handle_pdn_connectivity_request: Assertion `SECURITY_CONTEXT_IS_VALID(mme_ue)' failed. (esm_handler.c:29) +/home/acetcom/Documents/git/open5gs/nextepc/lib/ogslib/src/core/.libs/libogscore-1.0.so.0(ogs_abort+0x2b)[0x7f608518271b] +/home/acetcom/Documents/git/open5gs/nextepc/test/.libs/testepc(+0x92121)[0x55dc9e274121] +/home/acetcom/Documents/git/open5gs/nextepc/test/.libs/testepc(+0x4f5b9)[0x55dc9e2315b9] +``` + + +Remove all subscriber information using MongoDB Client +``` +$ mongo +> db.subscribers.find() ### Check the test subscriber +> db.subscribers.drop() ### Remove all subscriber +> db.subscribers.find() ### Check that all subscribers are empty +``` + +Kill all processes. +```bash +$ ps -ef | grep testepc +$ ps -ef | grep nextepc +$ sudo pkill -9 testepc +$ sudo pkill -9 nextepc-epcd ... +``` + +Execute `testepc` +```bash +$ ./test/testepc +``` + +#### My eNB does not support IPv6. + +Your eNodeB don't have to support IPv6. + +If the sgw.gtpu configuration does not have an IPv6 address, the eNodeB can use IPv4 to connect to the MME and SGW. If the sgw.gtpu setting has an IPv6 address, you can disable the IPv6 address as shown below. + +```yaml +parameter: + no_ipv6: true +``` + +**Note:** This parameter `no_ipv6` is only applied to EPC Elements such as MME, SGW, and so on. The parameter `no_ipv6` does not affect to UE. So, IPv6-enabled UE can connect to NextEPC LTE network. +{: .notice--warning} + +#### Unable to add new user by WebUI + +It might be a CSRF token mismatch error. +Please, delete corresponding cookies, cache, session data etc. + +#### Change Domain from localdomain to anything else + +You should configure the domain name on your computer. Otherwise, freeDiameter raise an error. + +#### How many of UEs can NextEPC support? + +See the [lib/base/types.h](https://github.com/{{ site.github_username }}/nextepc/blob/master/lib/base/types.h). + +``` +#define MAX_NUM_OF_ENB 128 +#define MAX_NUM_OF_UE 128 +#define MAX_NUM_OF_SESS 4 +#define MAX_NUM_OF_BEARER 4 +#define MAX_NUM_OF_TUNNEL 3 /* Num of Tunnel per Bearer */ +#define MAX_NUM_OF_PF 16 /* Num of Packet Filter per Bearer */ + +#define MAX_POOL_OF_UE (MAX_NUM_OF_ENB * MAX_NUM_OF_UE) +#define MAX_POOL_OF_SESS (MAX_POOL_OF_UE * MAX_NUM_OF_SESS) +#define MAX_POOL_OF_BEARER (MAX_POOL_OF_SESS * MAX_NUM_OF_BEARER) +#define MAX_POOL_OF_TUNNEL (MAX_POOL_OF_BEARER * MAX_NUM_OF_TUNNEL) +#define MAX_POOL_OF_PF (MAX_POOL_OF_BEARER * MAX_NUM_OF_PF) +#define MAX_POOL_OF_DIAMETER_SESS (MAX_POOL_OF_UE * MAX_NUM_OF_SESS) +``` + +Currently, the number of UE is limited to `128*128`. + +#### What is the Default Configuration? + +- Network + +``` +* MME + S1AP: listen on all address avaiable in system + GTP-C: listen on the first IP address in system + DIAMETER: 127.0.0.2 (No TLS) + +* SGW + GTP-C: 127.0.0.2 + GTP-U: listen on the first IP address in system + +* PGW + GTP-C: Both 127.0.0.3 and [::1] + GTP-U: Both 127.0.0.3 and [::1] + DIAMETER: 127.0.0.3 (No TLS) + +* HSS + DIAMETER: 127.0.0.4 (No TLS) + +* PCRF + DIAMETER: 127.0.0.5 (No TLS) +``` + +- GUMMEI, PLMN and TAC + +``` +* GUMMEI + PLMN ID - MNC: 001, MCC: 01 + MME Group : 2 + MME Code : 1 + +* TAI + PLMN ID - MNC: 001, MCC: 01 + TAC : 12345 +``` + +- Security + +``` +* Integrity : EIA1 - Snow 3G +* Ciphering : EEA0 - Nothing +``` + +- UE Network + +``` +* IPv4 : 45.45.0.1/16 +* IPv6 : cafe::1/64 +``` + +- DNS + +``` +* IPv4 + Primary : 8.8.8.8 + Secondary : 8.8.4.4 + +* IPv6 + Primary : 2001:4860:4860::8888 + Secondary : 2001:4860:4860::8844 +``` + +#### The parsing errors are caused by tab spaces in the configuration files. + +YAML forbids tabs. You should use space instead of tab in NextEPC configuration file. + +```markdown +YAML FAQ: Why does YAML forbid tabs? + +Tabs have been outlawed since they are treated differently by different editors and tools. And since indentation is so critical to proper interpretation of YAML, this issue is just too tricky to even attempt. Indeed Guido van Rossum of Python has acknowledged that allowing TABs in Python source is a headache for many people and that were he to design Python again, he would forbid them. +``` + +#### High CPU usage on idle + +This issue will not occur after v0.4.x version. + +#### Cross compilation setup + +By default, NextEPC is designed to support the Embedding System. To do so, we introduced pool-based memory management. Unfortunately, we have not tested NextEPC in an embedded environment. I tried to compile on the ARM architecture using Docker and run it with QEMU for your reference. + +- We'll use Debian Docker Environment. + +```bash +$ git clone -r https://github.com/acetcom/nextepc +$ cd nextepc/docker +$ DIST=debian docker-compose run dev +``` + +- In Docker Container + +```bash +acetcom@nextepc-dev:~$ sudo dpkg --add-architecture armel +acetcom@nextepc-dev:~$ sudo apt-get install libsctp-dev:armel libyaml-dev:armel libgnutls28-dev:armel libgcrypt-dev:armel libssl-dev:armel libmongoc-dev:armel libbson-dev:armel +acetcom@nextepc-dev:~$ sudo apt-get install crossbuild-essential-armel +acetcom@nextepc-dev:~$ sudo apt-get install qemu + +acetcom@nextepc-dev:~$ mkdir git +acetcom@nextepc-dev:~$ cd git/ +acetcom@nextepc-dev:~/git$ git clone https://github.com/acetcom/nextepc +acetcom@nextepc-dev:~/git$ cd nextepc/ +acetcom@nextepc-dev:~/git/nextepc$ autoreconf -if;./configure --prefix=`pwd`/install --host=arm-linux-gnueabi;make -j 2 +acetcom@nextepc-dev:~/git/nextepc$ make install +acetcom@nextepc-dev:~/git/nextepc$ qemu-arm .libs/nextepc-mmed +NextEPC daemon v0.3.10 - Oct 4 2018 13:24:24 + +[10/04 13:38:06.329] WARN: pid file /home/acetcom/git/nextepc/install/var/run/nextepc-mmed/pid overwritten -- Unclean shutdown of previous NextEPC run? (application.c:113) + PID[55780] : '/home/acetcom/git/nextepc/install/var/run/nextepc-mmed/pid' + File Logging : '/home/acetcom/git/nextepc/install/var/log/nextepc/nextepc.log' +qemu: Unsupported syscall: 345 +qemu: Unsupported syscall: 345 +qemu: Unsupported syscall: 345 +qemu: Unsupported syscall: 345 +qemu: Unsupported syscall: 345 +qemu: Unsupported syscall: 345 +qemu: Unsupported syscall: 345 +qemu: Unsupported syscall: 345 + MongoDB URI : 'mongodb://mongodb/nextepc' + Configuration : '/home/acetcom/git/nextepc/install/etc/nextepc/nextepc.conf' +[10/04 13:38:06.400] MME try to initialize +Unknown host QEMU_IFLA type: 40 +Unknown host QEMU_IFLA type: 41 +Unknown host QEMU_IFLA type: 40 +Unknown host QEMU_IFLA type: 41 +Unknown QEMU_IFLA_INFO_KIND ipip +Unknown host QEMU_IFLA type: 40 +Unknown host QEMU_IFLA type: 41 +Unknown QEMU_IFLA_INFO_KIND ip6tnl +Unknown host QEMU_IFLA type: 40 +Unknown host QEMU_IFLA type: 41 +Unknown host QEMU_IFLA type: 40 +Unknown host QEMU_IFLA type: 41 +Unknown host QEMU_IFLA type: 40 +Unknown host QEMU_IFLA type: 41 +Unknown host QEMU_IFLA type: 40 +Unknown host QEMU_IFLA type: 41 +Unknown QEMU_IFLA_INFO_KIND ipip +Unknown host QEMU_IFLA type: 40 +Unknown host QEMU_IFLA type: 41 +Unknown QEMU_IFLA_INFO_KIND ip6tnl +Unknown host QEMU_IFLA type: 40 +Unknown host QEMU_IFLA type: 41 +Unknown host QEMU_IFLA type: 40 +Unknown host QEMU_IFLA type: 41 +[10/04 13:38:08.693] gtp_server() [172.20.0.2]:2123 +[10/04 13:38:08.696] gtp_client() [127.0.0.2]:2123 +Unsupported setsockopt level=132 optname=11 +[10/04 13:38:08.697] ERRR: Unable to subscribe to SCTP events: (92:Protocol not available) (unix/sctp.c:291) +[10/04 13:38:08.699] ASSERT: !(rv == CORE_OK). (unix/sctp.c:33) +[10/04 13:38:08.700] ERRR: sctp_server() [172.20.0.2]:36412 failed(92:Protocol not available) (unix/sctp.c:98) +[10/04 13:38:08.701] ASSERT: !(rv == CORE_OK). (s1ap_sctp.c:35) +[10/04 13:38:08.702] ASSERT: !(rv == CORE_OK). (s1ap_path.c:53) +[10/04 13:38:08.703] ASSERT: !(rv == CORE_OK). (s1ap_path.c:28) +[10/04 13:38:08.704] ERRR: Can't establish S1AP path (mme_sm.c:63) +[10/04 13:38:08.708] MME initialize...done + + +[10/04 13:38:08.710] INFO: NextEPC daemon start (main.c:157) +``` + +The SCTP module is not included in the QEMU kernel. I believe that if the Linux kernel installed on your target platform contains an SCTP module, it will work normally. +{: .notice--warning} diff --git a/docs/_pages/github.md b/docs/_pages/github.md new file mode 100644 index 000000000..938785d70 --- /dev/null +++ b/docs/_pages/github.md @@ -0,0 +1,5 @@ +--- +title: GitHub +redirect_to: + - https://github.com/open5gs/nextepc +--- diff --git a/docs/_pages/home.md b/docs/_pages/home.md new file mode 100644 index 000000000..891e8db1d --- /dev/null +++ b/docs/_pages/home.md @@ -0,0 +1,5 @@ +--- +layout: allposts +list_title: News +permalink: / +--- diff --git a/docs/_posts/2017-02-01-programming-started.md b/docs/_posts/2017-02-01-programming-started.md new file mode 100644 index 000000000..fa675f9ce --- /dev/null +++ b/docs/_posts/2017-02-01-programming-started.md @@ -0,0 +1,9 @@ +--- +title: "Programming started." +date: 2017-02-01 19:48:49 +0900 +categories: + - Release +tags: + - News + - Release +--- diff --git a/docs/_posts/2017-10-09-release-v0.1.0.md b/docs/_posts/2017-10-09-release-v0.1.0.md new file mode 100644 index 000000000..317fda0cf --- /dev/null +++ b/docs/_posts/2017-10-09-release-v0.1.0.md @@ -0,0 +1,11 @@ +--- +title: "First version released." +date: 2017-10-09 11:03:15 +0900 +categories: + - Release +tags: + - News + - Release +--- + +- Assets -- [v0.1.0.tar.gz](https://github.com/acetcom/nextepc/archive/v0.1.0.tar.gz) diff --git a/docs/_posts/2017-10-11-release-v0.1.1.md b/docs/_posts/2017-10-11-release-v0.1.1.md new file mode 100644 index 000000000..704e8e5c3 --- /dev/null +++ b/docs/_posts/2017-10-11-release-v0.1.1.md @@ -0,0 +1,12 @@ +--- +title: "v0.1.1 - Fix the bug related to Mongo C Driver" +date: 2017-10-11 15:46:45 +0900 +categories: + - Release +tags: + - News + - Release +--- + +- Fix the bug related to Mongo C Driver. [[More Info](https://github.com/acetcom/nextepc/commit/4245502ae287df9c457621b3f4cccb519c4d4878)] +- Assets -- [v0.1.1.tar.gz](https://github.com/acetcom/nextepc/archive/v0.1.1.tar.gz) diff --git a/docs/_posts/2017-11-02-release-v0.2.0.md b/docs/_posts/2017-11-02-release-v0.2.0.md new file mode 100644 index 000000000..382d21203 --- /dev/null +++ b/docs/_posts/2017-11-02-release-v0.2.0.md @@ -0,0 +1,14 @@ +--- +title: "v0.2.0 - Package Publish, Support More OS" +date: 2017-11-02 17:21:21 +0900 +categories: + - Release +tags: + - News + - Release +--- + +- Publish Debian/Ubuntu Package +- Support FreeBSD and Mac OS X" + +- Assets -- [v0.2.0.tar.gz](https://github.com/acetcom/nextepc/archive/v0.2.0.tar.gz) diff --git a/docs/_posts/2017-12-18-release-v0.3.0.md b/docs/_posts/2017-12-18-release-v0.3.0.md new file mode 100644 index 000000000..15ec42a68 --- /dev/null +++ b/docs/_posts/2017-12-18-release-v0.3.0.md @@ -0,0 +1,14 @@ +--- +title: "v0.3.0 - IPv6 Support, Change Configuration File Format" +date: 2017-12-18 20:57:47 +0900 +categories: + - Release +tags: + - News + - Release +--- + +- IPv6 Support (Linux Only) +- Change Configuration File (JSON -> YAML) + +- Assets -- [v0.3.0.tar.gz](https://github.com/acetcom/nextepc/archive/v0.3.0.tar.gz) diff --git a/docs/_posts/2017-12-31-release-v0.3.1.md b/docs/_posts/2017-12-31-release-v0.3.1.md new file mode 100644 index 000000000..ed65a62af --- /dev/null +++ b/docs/_posts/2017-12-31-release-v0.3.1.md @@ -0,0 +1,12 @@ +--- +title: "v0.3.1 - Bug Fixes" +date: 2017-12-31 22:20:39 +0900 +categories: + - Release +tags: + - News + - Release +--- + +- Add Authentication Synch Failure ([#11](https://github.com/acetcom/nextepc/issues/11)) -- [eric80s](https://github.com/eric80s) +- Assets -- [v0.3.1.tar.gz](https://github.com/acetcom/nextepc/archive/v0.3.1.tar.gz) diff --git a/docs/_posts/2018-02-13-release-v0.3.3.md b/docs/_posts/2018-02-13-release-v0.3.3.md new file mode 100644 index 000000000..1ae1fc8df --- /dev/null +++ b/docs/_posts/2018-02-13-release-v0.3.3.md @@ -0,0 +1,20 @@ +--- +title: "v0.3.3 - Docker Support" +date: 2018-02-13 04:50:34 +0900 +categories: + - Release +tags: + - News + - Release +head_inline: "" +--- + +#### New +- Docker support ([#16](https://github.com/acetcom/nextepc/issues/16)) -- [amilenovic](https://github.com/amilenovic) + +#### Bug fixes +- Fix the BUG for MAC failure of authentication failure -- [#17](https://github.com/acetcom/nextepc/issues/17) from [razaborg](https://github.com/razaborg) +- Fix the BUG for EPS attach type of Attach accept -- [#12](https://github.com/acetcom/nextepc/issues/12) from [pcminitech](https://github.com/pcminitech) + +Download -- [v0.3.3.tar.gz](https://github.com/acetcom/nextepc/archive/v0.3.3.tar.gz) +{: .notice--info} diff --git a/docs/_posts/2018-03-06-release-v0.3.5.md b/docs/_posts/2018-03-06-release-v0.3.5.md new file mode 100644 index 000000000..ff0daa045 --- /dev/null +++ b/docs/_posts/2018-03-06-release-v0.3.5.md @@ -0,0 +1,20 @@ +--- +title: "v0.3.5 - Support Network Name" +date: 2018-03-06 12:43:34 +0900 +categories: + - Release +tags: + - News + - Release +head_inline: "" +--- + +#### New +- Support Network Name ([#22](https://github.com/acetcom/nextepc/pull/22)) -- [medeiros405](https://github.com/medeiros405) + +#### Bug fixes +- Fix the BUG for security capabilities mismatch ([#27](https://github.com/acetcom/nextepc/issues/27)) -- [wayne43290](https://github.com/wayne43290) +- Fix the BUG that SGW process is dead during paging process ([#18](https://github.com/acetcom/nextepc/issues/18)) -- [jackson040407](https://github.com/jackson040407) + +Download -- [v0.3.5.tar.gz](https://github.com/acetcom/nextepc/archive/v0.3.5.tar.gz) +{: .notice--info} diff --git a/docs/_posts/2018-03-21-release-v0.3.6.md b/docs/_posts/2018-03-21-release-v0.3.6.md new file mode 100644 index 000000000..8ec1425d0 --- /dev/null +++ b/docs/_posts/2018-03-21-release-v0.3.6.md @@ -0,0 +1,15 @@ +--- +title: "v0.3.6 - More S1AP Message" +date: 2018-03-21 08:05:56 +0900 +categories: + - Release +tags: + - News + - Release +--- + +- Support eNB/MME Configuration Transfer, Error Indication ([#29](https://github.com/acetcom/nextepc/issues/29)) -- [wayne43290](https://github.com/wayne43290) +- Increase SCTP recv buffer : 2048->8192 ([#35](https://github.com/acetcom/nextepc/issues/35)) -- [EugeneBogush](https://github.com/EugeneBogush) + +Download -- [v0.3.6.tar.gz](https://github.com/acetcom/nextepc/archive/v0.3.6.tar.gz) +{: .notice--info} diff --git a/docs/_posts/2018-04-14-release-v0.3.7.md b/docs/_posts/2018-04-14-release-v0.3.7.md new file mode 100644 index 000000000..fafb73949 --- /dev/null +++ b/docs/_posts/2018-04-14-release-v0.3.7.md @@ -0,0 +1,12 @@ +--- +title: "v0.3.7 - S1AP supports Release 14.4.0" +date: 2018-04-14 15:04:04 +0900 +categories: + - Release +tags: + - News + - Release +--- + +Download -- [v0.3.7.tar.gz](https://github.com/acetcom/nextepc/archive/v0.3.7.tar.gz) +{: .notice--info} diff --git a/docs/_posts/2018-06-03-release-v0.3.9.md b/docs/_posts/2018-06-03-release-v0.3.9.md new file mode 100644 index 000000000..d152f919d --- /dev/null +++ b/docs/_posts/2018-06-03-release-v0.3.9.md @@ -0,0 +1,23 @@ +--- +title: "v0.3.9 - Support NAS encryption" +date: 2018-06-03 01:42:05 +0000 +categories: + - Release +tags: + - News + - Release +head_inline: "" +--- + +#### New +- Support NAS encryption(EEA1/EEA2/EEA3) + +#### Bug fixes +- Confirm with 36.412 requirement of SCTP stream id ([#54](https://github.com/acetcom/nextepc/issues/54)) -- [brchiu](https://github.com/brchiu) +- Fix to set correct timezone in UE ([#61](https://github.com/acetcom/nextepc/pull/61)) -- [medeiros405](https://github.com/medeiros405) +- Fix to change MME's integrity order ([#64](https://github.com/acetcom/nextepc/issues/64)) -- [kewinrausch](https://github.com/kewinrausch) +- Fix the bug for tracking area update ([#29](https://github.com/acetcom/nextepc/issues/29)) -- [wayne43290](https://github.com/wayne43290) +- Fix the bug for indirect tunnel ([#29](https://github.com/acetcom/nextepc/issues/29)) -- [wayne43290](https://github.com/wayne43290) + +Download -- [v0.3.9.tar.gz](https://github.com/acetcom/nextepc/archive/v0.3.9.tar.gz) +{: .notice--info} diff --git a/docs/_posts/2018-08-17-release-v0.3.10.md b/docs/_posts/2018-08-17-release-v0.3.10.md new file mode 100644 index 000000000..8a7952829 --- /dev/null +++ b/docs/_posts/2018-08-17-release-v0.3.10.md @@ -0,0 +1,21 @@ +--- +title: "v0.3.10 - Bug Fixes" +date: 2018-08-17 04:28:50 +0000 +categories: + - Release +tags: + - News + - Release +head_inline: "" +--- + +#### Bug fixes + - Renew freeDiameter Certificate ([#93](https://github.com/acetcom/nextepc/issues/93), [#94](https://github.com/acetcom/nextepc/issues/94)) -- [Ravi-t](https://github.com/Ravi-t), [hchenji](https://github.com/hchenji) + - Fix TLV uint32 bug ([#73](https://github.com/acetcom/nextepc/pull/73)) -- [giuliol](https://github.com/giuliol) + - Add TRACE for IP address of connected UEs ([#71](https://github.com/acetcom/nextepc/issues/71)) -- [pgupta408](https://github.com/Ravi-t) + - Show reason string for YAML parser error ([#40](https://github.com/acetcom/nextepc/issues/40)) -- [Raw1mage](https://github.com/Raw1mage) + - Fix compile error for GCC 8.1 + - Fix compile error for Mongo-C-Driver 1.11 + +Download -- [v0.3.10.tar.gz](https://github.com/acetcom/nextepc/archive/v0.3.10.tar.gz) +{: .notice--info} diff --git a/docs/_posts/2019-04-27-release-v0.3.11.md b/docs/_posts/2019-04-27-release-v0.3.11.md new file mode 100644 index 000000000..6041803cb --- /dev/null +++ b/docs/_posts/2019-04-27-release-v0.3.11.md @@ -0,0 +1,28 @@ +--- +title: "v0.3.11 - First version in 2019" +date: 2019-04-27 10:30:00 +0900 +categories: + - Release +tags: + - News + - Release +head_inline: "" +--- + +#### Enhancements +- Support 4-bytes RES in NAS Auth ([#147](https://github.com/acetcom/nextepc/issues/147)) -- [Ranjjiitsingh](https://github.com/Ranjjiitsingh) +- Add SGW selection mode ([#100](https://github.com/acetcom/nextepc/pull/100)) -- [TerryAlu](https://github.com/TerryAlu) + +#### Bug fixes +- Increase packet memory buffer ([#161](https://github.com/acetcom/nextepc/issues/161)) -- [mathieuxilan](https://github.com/mathieuxilan) +- Fix setting END-ID to 0 ([#156](https://github.com/acetcom/nextepc/issues/156)) -- [hypercloud2017](https://github.com/hypercloud2017) +- Fix incorrect timezone in NAS & GTP ([#140](https://github.com/acetcom/nextepc/issues/140)) -- [EugeneBogush](https://github.com/EugeneBogush) + +#### Miscellaneous +- nas_message.py: Fix copy+paste error([#159](https://github.com/acetcom/nextepc/issues/159)) -- [laf0rge](https://github.com/laf0rge) +- Fix missing 'break' statement ([#129](https://github.com/acetcom/nextepc/pull/129)) -- [EugeneBogush](https://github.com/EugeneBogush) +- Add missing C namespace ([#109](https://github.com/acetcom/nextepc/pull/109)) -- [brchiu](https://github.com/brchiu) +- Refine EXIT routine for daemon process + +Download -- [v0.3.11.tar.gz](https://github.com/acetcom/nextepc/archive/v0.3.11.tar.gz) +{: .notice--info} diff --git a/docs/_sass/minima-reboot.scss b/docs/_sass/minima-reboot.scss new file mode 100644 index 000000000..b3665363e --- /dev/null +++ b/docs/_sass/minima-reboot.scss @@ -0,0 +1,6 @@ +@charset "utf-8"; + +@import + "minima-reboot/layout", + "minima-reboot/syntax-highlighting" +; diff --git a/docs/_sass/minima-reboot/_layout.scss b/docs/_sass/minima-reboot/_layout.scss new file mode 100644 index 000000000..63799e736 --- /dev/null +++ b/docs/_sass/minima-reboot/_layout.scss @@ -0,0 +1,73 @@ +$layout-color-border: #e9ecef !default; +$layout-color-background: #fdfdfd !default; +$layout-color-syntax-highlighting-background: #f8f9fa !default; +$layout-responsive-nav-cutoff: 768px !default; + +body { + background-color: $layout-color-background; +} + +#nav-header { + border-top: 5px solid !important; + border-bottom: 1px solid $layout-color-border !important; +} + +#nav-container { + height: 3.5rem; +} + +#nav-menu-container { + @media (max-width: $layout-responsive-nav-cutoff - 1) { + position: absolute; + z-index: 100; + top: 0.4375rem; + right: 1rem; + border: 1px solid $layout-color-border; + border-radius: .25rem; + background-color: $layout-color-background; + } +} + +#nav-trigger-label { + height: 1.5rem; + width: 1.5rem; + cursor: pointer; +} + +#nav-trigger:checked ~ #nav-menu { + display: block !important; +} + +#content { + h1, h2, h3, h4 { + margin-top: 2rem; + margin-bottom: 1rem; + text-align: left; + } + blockquote { + border-left: 4px solid $layout-color-border; + padding-left: 1rem; + font-style: italic + } + .footnotes { + text-align: left; + } + #rss-icon { + height: 1.5rem; + width: 1.5rem; + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='rgb(134, 142, 150)' d='M119.9,336.1c-30.8,0-55.9,25.1-55.9,55.8c0,30.8,25.1,55.6,55.9,55.6c30.9,0,55.9-24.9,55.9-55.6,C175.8,361.2,150.8,336.1,119.9,336.1z'/%3E%3Cpath fill='rgb(134, 142, 150)' d='M64,192v79.9c48,0,94.1,14.2,128,48.1c33.9,33.9,48,79.9,48,128h80C320,308.1,204,192,64,192z'/%3E%3Cpath fill='rgb(134, 142, 150)' d='M64,64v79.9c171,0,303.9,133,303.9,304.1H448C448,236.3,276,64,64,64z'/%3E%3C/svg%3E") + } +} + +#site-footer { + border-top: 1px solid $layout-color-border; +} + +.highlight pre { + border: 1px solid $layout-color-border; + border-radius: .25rem; + background-color: $layout-color-syntax-highlighting-background; + padding: 0.75rem; + margin-bottom: 1rem; + white-space: pre-wrap; +} diff --git a/docs/_sass/minima-reboot/_syntax-highlighting.scss b/docs/_sass/minima-reboot/_syntax-highlighting.scss new file mode 100644 index 000000000..302e534d7 --- /dev/null +++ b/docs/_sass/minima-reboot/_syntax-highlighting.scss @@ -0,0 +1,61 @@ +.highlight { + .c { color: #998; font-style: italic } // Comment + .err { color: #a61717; background-color: #e3d2d2 } // Error + .k { font-weight: bold } // Keyword + .o { font-weight: bold } // Operator + .cm { color: #998; font-style: italic } // Comment.Multiline + .cp { color: #999; font-weight: bold } // Comment.Preproc + .c1 { color: #998; font-style: italic } // Comment.Single + .cs { color: #999; font-weight: bold; font-style: italic } // Comment.Special + .gd { color: #000; background-color: #fdd } // Generic.Deleted + .gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific + .ge { font-style: italic } // Generic.Emph + .gr { color: #a00 } // Generic.Error + .gh { color: #999 } // Generic.Heading + .gi { color: #000; background-color: #dfd } // Generic.Inserted + .gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific + .go { color: #888 } // Generic.Output + .gp { color: #555 } // Generic.Prompt + .gs { font-weight: bold } // Generic.Strong + .gu { color: #aaa } // Generic.Subheading + .gt { color: #a00 } // Generic.Traceback + .kc { font-weight: bold } // Keyword.Constant + .kd { font-weight: bold } // Keyword.Declaration + .kp { font-weight: bold } // Keyword.Pseudo + .kr { font-weight: bold } // Keyword.Reserved + .kt { color: #458; font-weight: bold } // Keyword.Type + .m { color: #099 } // Literal.Number + .s { color: #d14 } // Literal.String + .na { color: #008080 } // Name.Attribute + .nb { color: #0086B3 } // Name.Builtin + .nc { color: #458; font-weight: bold } // Name.Class + .no { color: #008080 } // Name.Constant + .ni { color: #800080 } // Name.Entity + .ne { color: #900; font-weight: bold } // Name.Exception + .nf { color: #900; font-weight: bold } // Name.Function + .nn { color: #555 } // Name.Namespace + .nt { color: #000080 } // Name.Tag + .nv { color: #008080 } // Name.Variable + .ow { font-weight: bold } // Operator.Word + .w { color: #bbb } // Text.Whitespace + .mf { color: #099 } // Literal.Number.Float + .mh { color: #099 } // Literal.Number.Hex + .mi { color: #099 } // Literal.Number.Integer + .mo { color: #099 } // Literal.Number.Oct + .sb { color: #d14 } // Literal.String.Backtick + .sc { color: #d14 } // Literal.String.Char + .sd { color: #d14 } // Literal.String.Doc + .s2 { color: #d14 } // Literal.String.Double + .se { color: #d14 } // Literal.String.Escape + .sh { color: #d14 } // Literal.String.Heredoc + .si { color: #d14 } // Literal.String.Interpol + .sx { color: #d14 } // Literal.String.Other + .sr { color: #009926 } // Literal.String.Regex + .s1 { color: #d14 } // Literal.String.Single + .ss { color: #990073 } // Literal.String.Symbol + .bp { color: #999 } // Name.Builtin.Pseudo + .vc { color: #008080 } // Name.Variable.Class + .vg { color: #008080 } // Name.Variable.Global + .vi { color: #008080 } // Name.Variable.Instance + .il { color: #099 } // Literal.Number.Integer.Long +} diff --git a/docs/_sass/minimal-mistakes.scss b/docs/_sass/minimal-mistakes.scss new file mode 100644 index 000000000..e2cb364ac --- /dev/null +++ b/docs/_sass/minimal-mistakes.scss @@ -0,0 +1,11 @@ +/*! + * Minimal Mistakes Jekyll Theme 4.15.2 by Michael Rose + * Copyright 2013-2019 Michael Rose - mademistakes.com | @mmistakes + * Licensed under MIT (https://github.com/mmistakes/minimal-mistakes/blob/master/LICENSE) +*/ + +/* Variables */ +@import "minimal-mistakes/variables"; + +/* Components */ +@import "minimal-mistakes/notices"; diff --git a/docs/_sass/minimal-mistakes/_notices.scss b/docs/_sass/minimal-mistakes/_notices.scss new file mode 100644 index 000000000..0a0e753d7 --- /dev/null +++ b/docs/_sass/minimal-mistakes/_notices.scss @@ -0,0 +1,100 @@ +/* ========================================================================== + NOTICE TEXT BLOCKS + ========================================================================== */ + +/** + * Default Kramdown usage (no indents!): + *
+ * #### Headline for the Notice + * Text for the notice + *
+ */ + +@mixin notice($notice-color) { + margin: 1em 0 !important; /* override*/ + padding: 1em; + color: $dark-gray; + font-family: $global-font-family; + font-size: $type-size-6 !important; + text-indent: initial; /* override*/ + background-color: mix(#fff, $notice-color, 90%); + border-radius: $border-radius; + box-shadow: 0 1px 1px rgba($notice-color, 0.25); + + h4 { + margin-top: 0 !important; /* override*/ + margin-bottom: 0.75em; + } + + @at-root .page__content #{&} h4 { + /* using at-root to override .page-content h4 font size*/ + margin-bottom: 0; + font-size: 1em; + } + + p { + &:last-child { + margin-bottom: 0 !important; /* override*/ + } + } + + h4 + p { + /* remove space above paragraphs that appear directly after notice headline*/ + margin-top: 0; + padding-top: 0; + } + + a { + color: $notice-color; + + &:hover { + color: mix(#000, $notice-color, 40%); + } + } + + code { + background-color: mix(#fff, $notice-color, 95%) + } + + ul { + &:last-child { + margin-bottom: 0; /* override*/ + } + } +} + +/* Default notice */ + +.notice { + @include notice($light-gray); +} + +/* Primary notice */ + +.notice--primary { + @include notice($primary-color); +} + +/* Info notice */ + +.notice--info { + @include notice($info-color); +} + +/* Warning notice */ + +.notice--warning { + @include notice($warning-color); +} + +/* Success notice */ + +.notice--success { + @include notice($success-color); +} + +/* Danger notice */ + +.notice--danger { + @include notice($danger-color); +} diff --git a/docs/_sass/minimal-mistakes/_variables.scss b/docs/_sass/minimal-mistakes/_variables.scss new file mode 100644 index 000000000..fe0ca3680 --- /dev/null +++ b/docs/_sass/minimal-mistakes/_variables.scss @@ -0,0 +1,159 @@ +/* ========================================================================== + Variables + ========================================================================== */ + +/* + Typography + ========================================================================== */ + +$doc-font-size: 16 !default; + +/* paragraph indention */ +$paragraph-indent: false !default; // true, false (default) +$indent-var: 1.3em !default; + +/* system typefaces */ +$serif: Georgia, Times, serif !default; +$sans-serif: -apple-system, BlinkMacSystemFont, "Roboto", "Segoe UI", + "Helvetica Neue", "Lucida Grande", Arial, sans-serif !default; +$monospace: Monaco, Consolas, "Lucida Console", monospace !default; + +/* sans serif typefaces */ +$sans-serif-narrow: $sans-serif !default; +$helvetica: Helvetica, "Helvetica Neue", Arial, sans-serif !default; + +/* serif typefaces */ +$georgia: Georgia, serif !default; +$times: Times, serif !default; +$bodoni: "Bodoni MT", serif !default; +$calisto: "Calisto MT", serif !default; +$garamond: Garamond, serif !default; + +$global-font-family: $sans-serif !default; +$header-font-family: $sans-serif !default; +$caption-font-family: $serif !default; + +/* type scale */ +$type-size-1: 2.441em !default; // ~39.056px +$type-size-2: 1.953em !default; // ~31.248px +$type-size-3: 1.563em !default; // ~25.008px +$type-size-4: 1.25em !default; // ~20px +$type-size-5: 1em !default; // ~16px +$type-size-6: 0.9em !default; // ~12px +$type-size-7: 0.6875em !default; // ~11px +$type-size-8: 0.625em !default; // ~10px + +/* + Colors + ========================================================================== */ + +$gray: #7a8288 !default; +$dark-gray: mix(#000, $gray, 40%) !default; +$darker-gray: mix(#000, $gray, 60%) !default; +$light-gray: mix(#fff, $gray, 50%) !default; +$lighter-gray: mix(#fff, $gray, 90%) !default; + +$background-color: #fff !default; +$code-background-color: #fafafa !default; +$code-background-color-dark: $light-gray !default; +$text-color: $dark-gray !default; +$muted-text-color: mix(#fff, $text-color, 35%) !default; +$border-color: $lighter-gray !default; +$form-background-color: $lighter-gray !default; +$footer-background-color: $lighter-gray !default; + +$primary-color: #6f777d !default; +$success-color: #3fa63f !default; +$warning-color: #d67f05 !default; +$danger-color: #ee5f5b !default; +$info-color: #3b9cba !default; +$focus-color: $primary-color !default; +$active-color: mix(#fff, $primary-color, 80%) !default; + +/* YIQ color contrast */ +$yiq-contrasted-dark-default: $dark-gray !default; +$yiq-contrasted-light-default: #fff !default; +$yiq-contrasted-threshold: 175 !default; +$yiq-debug: false !default; + +/* brands */ +$behance-color: #1769ff !default; +$bitbucket-color: #205081 !default; +$dribbble-color: #ea4c89 !default; +$facebook-color: #3b5998 !default; +$flickr-color: #ff0084 !default; +$foursquare-color: #0072b1 !default; +$github-color: #171516 !default; +$gitlab-color: #e24329 !default; +$instagram-color: #517fa4 !default; +$lastfm-color: #d51007 !default; +$linkedin-color: #007bb6 !default; +$mastodon-color: #2b90d9 !default; +$pinterest-color: #cb2027 !default; +$reddit-color: #ff4500 !default; +$rss-color: #fa9b39 !default; +$soundcloud-color: #ff3300 !default; +$stackoverflow-color: #fe7a15 !default; +$tumblr-color: #32506d !default; +$twitter-color: #55acee !default; +$vimeo-color: #1ab7ea !default; +$vine-color: #00bf8f !default; +$youtube-color: #bb0000 !default; +$xing-color: #006567 !default; + +/* links */ +$link-color: mix(#000, $info-color, 15%) !default; +$link-color-hover: mix(#000, $link-color, 25%) !default; +$link-color-visited: mix(#fff, $link-color, 15%) !default; +$masthead-link-color: $primary-color !default; +$masthead-link-color-hover: mix(#000, $primary-color, 25%) !default; +$navicon-link-color-hover: mix(#fff, $primary-color, 75%) !default; + +/* syntax highlighting (base16) */ +$base00: #263238 !default; +$base01: #2e3c43 !default; +$base02: #314549 !default; +$base03: #546e7a !default; +$base04: #b2ccd6 !default; +$base05: #eeffff !default; +$base06: #eeffff !default; +$base07: #ffffff !default; +$base08: #f07178 !default; +$base09: #f78c6c !default; +$base0a: #ffcb6b !default; +$base0b: #c3e88d !default; +$base0c: #89ddff !default; +$base0d: #82aaff !default; +$base0e: #c792ea !default; +$base0f: #ff5370 !default; + +/* + Breakpoints + ========================================================================== */ + +$small: 600px !default; +$medium: 768px !default; +$medium-wide: 900px !default; +$large: 1024px !default; +$x-large: 1280px !default; + +/* + Grid + ========================================================================== */ + +$right-sidebar-width-narrow: 200px !default; +$right-sidebar-width: 300px !default; +$right-sidebar-width-wide: 400px !default; + +/* + Other + ========================================================================== */ + +$border-radius: 4px !default; +$box-shadow: 0 1px 1px rgba(0, 0, 0, 0.125) !default; +$nav-height: 2em !default; +$nav-toggle-height: 2rem !default; +$navicon-width: 1.5rem !default; +$navicon-height: 0.25rem !default; +$global-transition: all 0.2s ease-in-out !default; +$intro-transition: intro 0.3s both !default; diff --git a/docs/assets/css/main.scss b/docs/assets/css/main.scss new file mode 100644 index 000000000..3728137d8 --- /dev/null +++ b/docs/assets/css/main.scss @@ -0,0 +1,5 @@ +--- +--- + +@import "minima-reboot"; +@import "minimal-mistakes"; diff --git a/docs/assets/favicon.ico b/docs/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..f929258204ad6aa28856b37bb06d7ee967186c5d GIT binary patch literal 1150 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 /dev/null && tput colors) # supports color + if test -n "$ncolors" && test $ncolors -ge 8; then + termcols=$(tput cols) + bold="$(tput bold)" + underline="$(tput smul)" + standout="$(tput smso)" + normal="$(tput sgr0)" + black="$(tput setaf 0)" + red="$(tput setaf 1)" + green="$(tput setaf 2)" + yellow="$(tput setaf 3)" + blue="$(tput setaf 4)" + magenta="$(tput setaf 5)" + cyan="$(tput setaf 6)" + white="$(tput setaf 7)" + fi +fi + +print_bold() { + title="$1" + text="$2" + + echo + echo "${red}================================================================================${normal}" + echo "${red}================================================================================${normal}" + echo + echo -e " ${bold}${yellow}${title}${normal}" + echo + echo -en " ${text}" + echo + echo "${red}================================================================================${normal}" + echo "${red}================================================================================${normal}" +} + +bail() { + echo 'Error executing command, exiting' + exit 1 +} + +exec_cmd_nobail() { + echo "+ $1" + bash -c "$1" +} + +exec_cmd() { + exec_cmd_nobail "$1" || bail +} + +uninstall() { +exec_cmd_nobail "deb-systemd-invoke stop nextepc-webui" +exec_cmd_nobail "systemctl disable nextepc-webui" +exec_cmd_nobail "rm -f /lib/systemd/system/${PACKAGE}-webui.service" +exec_cmd_nobail "systemctl daemon-reload" + +exec_cmd "rm -rf ./${PACKAGE}-${VERSION}" +exec_cmd "rm -rf /usr/lib/node_modules/${PACKAGE}" +} + +install() { + +PRE_INSTALL_PKGS="" + +if [ ! -x /usr/bin/mongod ]; then + PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} mongodb" +fi + +if [ ! -x /usr/bin/node ] && [ ! -x /usr/bin/wget ]; then + PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} nodejs" +fi + +if [ ! -x /usr/bin/curl ] && [ ! -x /usr/bin/wget ]; then + PRE_INSTALL_PKGS="${PRE_INSTALL_PKGS} curl" +fi + +print_status "Populating apt-get cache..." +exec_cmd 'apt-get update' + +if [ "X${PRE_INSTALL_PKGS}" != "X" ]; then + print_status "Installing packages required for setup:${PRE_INSTALL_PKGS}..." + # This next command needs to be redirected to /dev/null or the script will bork + # in some environments + exec_cmd "apt-get install -y${PRE_INSTALL_PKGS} > /dev/null 2>&1" +fi + +uninstall + +print_status "Download the NextEPC Source Code (v${VERSION})..." +if [ -x /usr/bin/curl ]; then + exec_cmd "curl -sLf 'https://github.com/acetcom/${PACKAGE}/archive/v${VERSION}.tar.gz' | tar zxf -" + RC=$? +else + exec_cmd "wget -qO- /dev/null 'https://github.com/acetcom/${PACKAGE}/archive/v${VERSION}.tar.gz' | tar zxf -" + RC=$? +fi + +print_status "Build the NextEPC WebUI..." +exec_cmd "cd ./${PACKAGE}-${VERSION}/webui && npm install && npm run build" + +print_status "Install the NextEPC WebUI..." +exec_cmd "mv ./${PACKAGE}-${VERSION}/webui /usr/lib/node_modules/${PACKAGE}" +exec_cmd_nobail "chown -R nextepc:nextepc /usr/lib/node_modules/${PACKAGE}" + +exec_cmd "cat << EOF > /lib/systemd/system/nextepc-webui.service +[Unit] +Description=NextEPC WebUI +BindTo=mongodb.service +After=networking.service mongodb.service + +[Service] +Type=simple + +WorkingDirectory=/usr/lib/node_modules/nextepc +Environment=NODE_ENV=production +ExecStart=/usr/bin/node server/index.js +Restart=always +RestartSec=2 + +[Install] +WantedBy=multi-user.target +EOF" + +exec_cmd_nobail "systemctl daemon-reload" +exec_cmd "systemctl enable nextepc-webui" +exec_cmd "deb-systemd-invoke start nextepc-webui" + +exec_cmd "rm -rf ./${PACKAGE}-${VERSION}" +} + +## Defer setup until we have the complete script +install diff --git a/docs/assets/webui/uninstall b/docs/assets/webui/uninstall new file mode 100644 index 000000000..127c902ae --- /dev/null +++ b/docs/assets/webui/uninstall @@ -0,0 +1,27 @@ +#!/bin/bash + +# Refer to Node.js install script +# +# Run as root or insert `sudo -E` before `bash`: +# +# curl -sL http://nextepc.org/static/uninstall_webui.sh | bash - +# or +# wget -qO- http://nextepc.org/static/uninstall_webui.shx | bash - +# + +exec_cmd_nobail() { + echo "+ $1" + bash -c "$1" +} + +uninstall() { +exec_cmd_nobail "deb-systemd-invoke stop nextepc-webui" +exec_cmd_nobail "systemctl disable nextepc-webui" +exec_cmd_nobail "rm -f /lib/systemd/system/nextepc-webui.service" +exec_cmd_nobail "rm -rf /usr/lib/node_modules/nextepc" + +exec_cmd_nobail "systemctl daemon-reload" +} + +## Defer setup until we have the complete script +uninstall